viernes, 27 de agosto de 2010

Gestión de conexiones múltiples de bases de datos, en Python, y 2

Hola. En este post vamos a terminar de ver cómo crear un sistema de gestión de conexiones múltiples de bases de datos, en Python. Como se comentó en el anterior artículo, este tipo de diseño o proyecto si se quiere llamar así, se puede realizar con un ORM como SQLAlchemy (del cual estoy preparando un artículo muy interesante, en español, sobre su funcionamiento, y espero, si mi hijo me deja, publicarlo cuanto antes), o de cualquier otra manera. En este caso, utilizamos clases y orientación a objetos para realizar el trabajo.

Quizás el lector crea que utilizar clases implica que se está programando con el paradigma de POO. El caso es que no es así, ya que se pueden utilizar clases pero sin la filosofía del paradigma que lo contempla. Por ejemplo, una clase con un único método no es utilizar POO de forma correcta, aunque sea una clase, es una función mágica ó una pretty function, encapsulada en una clase, pero no es POO. Esto puede ser algo extraño pero la realidad es que es así.

Bueno, vamos a trabajar....

En el artículo anterior creamos una clase que gestiona "algo", que está compuesto por una colección de objetos identificados de forma unívoca por cadenas de caracteres. Si el lector se percata de ello, no se ha escrito ni una línea de código de nada referente al acceso a bases de datos, solamente una clase que gestiona una abstracción.

Para ver como funciona esta clase vamos a crear otra que gestione la recuperación de registros (filas) de tablas de bases de datos, de las cuales tenemos configuradas sus conexiones. Esto que parece algo trivial en realidad no lo es tanto, ya que con esta técnica podríamos obtener, por ejemplo, filas de tablas de diferentes orígenes, mezclándolas en una lista de tuplas (donde cada tupla es una fila de una tabla). Imaginemos por ejemplo, que queremos obtener datos de clientes de diferentes bases de datos, para homogeneizar sus atributos, o analizar ciertas codificaciones, ver la repetición de códigos de clientes, comparativas, sacar datos de diferentes orígenes para exportarlos a Excel ó CSV, etc.

A continuación vamos a diseñar una clase que utiliza la clase conexion (vista en el post anterior) para gestionar las conexiones que hagamos a los distintos orígenes de datos.

Nuestra clase se llamará lanzar_sql, y estará compuesta por dos atributos, uno de ellos una instanciación de la clase conexion y otra una lista que contendrá las tuplas (filas) de una selección de sentencias SQL. Esta nueva clase lo que hará será obtener filas de las tablas de las posibles conexiones creadas. De esta manera podemos manipular los resultados en una estructura. Veamos esto mejor con un gráfico:

Esto es, podemos ver que el atributo conexiones es una instanciación de la clase conexion. Así tendremos que nuestra clase lanzar_sql tendrá la información tal como así:

La implementación es:


class lanzar_sql(object):
def __init__(self):
self.__registros = []
self.conexiones = conexion()

def lanzar_sql(self,cadena_sql, nombre_conexion = None):
nombre_conexiones = self.conexiones.listar_conexiones()
if nombre_conexion is None:
for i in nombre_conexiones:
(conexion, filas ) =\
self.__lanzar_sentencia_sql(cadena_sql, i)
if f_esvacio(conexion) or f_esvacio(filas): break
self.__registros.append((conexion,filas))
else:
(conexion, filas ) =\
self.__lanzar_sentencia_sql(cadena_sql, \
nombre_conexion)

if f_esvacio(conexion) or f_esvacio(filas): pass
else: self.__registros.append((conexion,filas))

def __lanzar_sentencia_sql(self, cadena_sql, nombre_conexion):
nombre_conexiones = self.conexiones.listar_conexiones()
for i in nombre_conexiones:
if str(i).strip() == str(nombre_conexion).strip():
objeto_conexion =\
self.conexiones.devolver_conexion(i)
cursor = objeto_conexion.cursor()
# Lanzamos SQL.
aux = []
filas = cursor.execute(cadena_sql)
for j in filas: aux.append(j)
cursor.close()
# Devolvemos filas.
return nombre_conexion, aux
# Nos vamos.
return None, None

def devolver_registros(self):
return self.__registros

def limpiar_registros(self):
self.__registros = []
return True



Explicación de la clase lanzar_sql

Esta clase, como se ha comentado anteriormente tiene 2 atributos para guardar la información de conexiones e información recuperada y 4 métodos que pasamos a comentar:

lanzar_sql: Método que lanza la sentencia SQL (se espera una SELECT por diseño) pasada como parámetro y recupera (y guarda) las filas devueltas por la sentencia. Si no se especifica una nombre de conexión al que haga referencia (la conexión a la base de datos que queremos que haga el SELECT) por defecto se aplica a todas las conexiones. Esto es, si tenemos en varios orígenes de datos una tabla TABLA1, y hacemos lanzar_sql("SELECT * FROM TABLA1"), enviará dicha sentencia SQL a todas las conexiones guardadas en la clase (en realidad en la instanciación del objeto, pero por ser pedagógico hay veces que fuerzo el vocabulario conceptual).

__lanzar_sentencia_sql: Método privado que utiliza el método lanzar_sql. El lector debe de darse cuenta que dicho código se repite varias veces en lanzar_sql, por lo que es mejor sacarlo fuera como un método auxiliar, de manera que nos ahorramos código y lo hacemos más legible (¡la legibilidad cuenta, y mucho!).

devolver_registros: Método que devuelve la lista de registros (filas de las tablas) que contiene el objeto. ¿Y cómo distinguir las filas de los orígenes? Pues porque cada elemento de la lista está compuesto por dos subelementos, el nombre de la conexión y una tupla (fila de la tabla). Esto que parece algo confuso lo veremos a continuación en un ejemplo mucho más claro.

limpiar_registros: Método para eliminar las filas recuperadas.

Ejemplo

Puede que no esté claro el funcionamiento de esta clase, así que para dejar claro el asunto vamos a crear un ejemplo de acceso a dos bases de datos Microsoft Access, que atacaremos mediante pyodbc, y lo gestionaremos todo mediante la clase lanzar_sql, que utiliza la clase conexion.

Creamos una base de datos BD1.mdb, con las tablas TABLA_BD1 Y TABLA_BD, tal como sigue:


Creamos una base de datos BD2.mdb, con las tablas TABLA_BD2 Y TABLA_BD, tal como sigue:


Creamos sus correspondientes conexiones ODBC:


Imaginemos que las dos clases que hemos creado hasta el momento, conexion y lanzar_sql, están en un fichero denominado conexiones.py.

NOTA: Se tiene que tener instalado pyodbc. En el área de descarga solamente hay que elegir la versión de Python que tenemos. En mi caso Python 2.5.4. (Algún día hablaré el porqué no he migrado todavía a Python 3, aunque creo que el lector sabrá porqué argumento esto...).


Cargamos las clases creadas y el módulo para atacar las bases de datos vía ODBC:

from conexiones import *
import pyodbc

Abrimos conexiones:

conexion1 = pyodbc.connect('DSN=BD1;UID=;PWD=')
conexion2 = pyodbc.connect('DSN=BD2;UID=;PWD=')

Creamos un objeto conexión:

conexion = lanzar_sql()

Agregamos conexiones de varias bases de datos.

conexion.conexiones.agregar_conexion('access1',conexion1)
conexion.conexiones.agregar_conexion('access2',conexion2)

NOTA: El lector debe de darse cuenta de que el método agregar_conexion tiene su origen en la clase conexion, que ha sido instanciada en un atributo de la clase lanzar_sql. ¡Esto sí que es programación orientada a objetos! Esto es, aunque no hemos utilizado la herencia, que es uno de los mecanismos fundamentales de la POO, sí que lo hemos hecho de la encapsulación y abstracción.

Si queremos ver las conexiones que tenemos activas basta con:

print conexion.conexiones.listar_conexiones()

Y nos devuelve:

['access1', 'access2']
Pulse cualquier tecla para continuar

Lanzamos sentencias SQL SELECT:

conexion.lanzar_sql('SELECT * FROM TABLA_BD1','access1')
conexion.lanzar_sql('SELECT * FROM TABLA_BD2','access2')

Sacamos por pantalla los registros con la etiqueta de la conexión a la que pertenecen.

print "DATOS:"
registros = conexion.devolver_registros()
for i in registros: print i

Y obtenemos:

DATOS:
('access1', [(1, u'1', u'1', u'1', u'1', u'1')])
('access2', [(1, u'2', u'2', u'2', u'2', u'2')])
Pulse cualquier tecla para continuar

Si queremos solos las filas, sin los identificadores de conexión, para una exportación a CSV, por ejemplo, únicamente habría que espscificar el elemento i[1], tal que así:

registros = conexion.devolver_registros()
for i in registros: print i[1]

Devolviendo:

[(1, u'1', u'1', u'1', u'1', u'1')]
[(1, u'2', u'2', u'2', u'2', u'2')]

Podemos borrar los resultados con:

conexion.limpiar_registros()

Para finalizar vamos a lanzar una única sentencia SELECT SQL para todas las conexiones:

conexion.lanzar_sql('SELECT * FROM TABLA_BD')

Para obtener los registros con la etiqueta de la conexión a la que pertenecen.

registros = conexion.devolver_registros()
for i in registros: print i

Obteniendo el siguiente resultado:

('access1', [(1, u'a', u'a', u'a', u'a', u'a'), (2, u'b', u'b', u'b', u'b', u'b')])
('access2', [(1, u'c', u'c', u'c', u'c', u'c'), (2, u'd', u'd', u'd', u'd', u'd')])
Pulse cualquier tecla para continuar

Siempre que finalicemos habrá que cerrar las conexiones, tal que así:

conexion1.close()
conexion2.close()

CONCLUSIONES

En estos dos posts hemos visto como crear un sistema de gestión de conexiones múltiples de bases de datos, utilizando POO, en este caso la encapsulación y abstracción. Hemos creado dos clases, conexion y lanzar_sql, en donde esta última utiliza a la primera para implementar el mecanismo de gestión de conexiones. Decir que las clases creadas contienen los atributos y métodos mínimos para su funcionamiento. Se deja al lector su ampliación, ya que su utilidad puede ser importante si se implementan nuevos métodos y funcionalidades a partir de la base expuesta.

Se ha presentado un ejemplo con bases de datos Access para la obtención de información. Aunque es muy simple su uso, se abre todo un abanico de posibilidades de utilización y manejo de datos de diferentes orígenes, utilizando únicamente algunas de las estructuras que nos ofrece Python, esto es, listas y tuplas.

Evidentemente, hay en el mercado paquetes de software, como SQLAlchemy, con el que se pueden realizar este tipo de tareas, pero con estos artículos quiero demostrar que si se saben ciertas nociones de POO (tampoco hay que ser un experto), así como qué es lo que se quiere llegar a obtener, Python es una herramienta fantástica, con unos recursos de implementación realmente impresionantes.

Espero os sirvan estos artículos. Saludos.

martes, 24 de agosto de 2010

Gestión de conexiones múltiples de bases de datos, en Python, y 1


Hola. En este post vamos a ver una manera (de tantas de las que puede haber) de gestionar varias conexiones a una misma o distintas bases de datos, en Python, mediante la creación de una clase que se ocupe de este trabajo.

La gestión de conexiones tiene que ser independiente de donde se conecte, ya que de lo contrario no tendría gracia (¡imagine el lector cuantos SGBD hay en el mercado!), y sería un trabajo de chinos (sin desmerecer a ese gran país).

La cuestión es crear una clase que gestione "algo", que serán conexiones a bases de datos en este caso, identificadas por nombres unívocos (esto es, no podrán repetirse), aunque puede haber varias conexiones hacia un mismo origen de datos (algo muy frecuente en las problemáticas de sistemas de gestión).

Lo que se pretende es crear la clase conexion, que va a abstraer el manejo de objetos conexión, de manera que podamos aislar la clase y ser lo más asépticos posibles. Aquí la cuestión es principal es la reutilización de código, es decir, si creamos una clase que sea independiente de cualquier SGBD (sistema gestor de bases de datos), podremos utilizarla en cualquiera de nuestros proyectos, sin temor a tener que versionar o reescribir código de nuevo.

Imaginemos que tenemos 3 bases de datos diferentes, en los siguientes sistemas:

- Base de datos BD1 en un SGBD MySQL.
- Base de datos BD2 en un SGBD Firebird.
- Base de datos BD4 en un SGBD pysqlite.

Queremos crear una conexión a nuestra base de datos denominada BD1 de MySQL, además de crear tres conexiones a nuestra base de datos BD2 de Firebird. Sin embargo en este caso tenemos que conectar por un problema de licencias (es un ejemplo, no es el caso) una conexión por kinterbasdb y dos conexiones por ODBC (pyodbc). Y por último una conexión a BD4 a través de pysqlite.

¿Cómo gestionar tantas conexiones? ¿Sería posible incluirlas todas en una única estructura de manera que fuese sencilla su utilización? El problema expuesto tendría esta forma:

Definiendo la clase conexion

Si analizamos la problemática que se nos plantea e intentamos solucionarla con programación orientada a objetos, evidentemente pensamos en la creación de una clase, que debe de implementar, como atributo principal, una lista de conexiones.

Esta lista de conexiones debe de poderse enumerar de alguna manera, esto es, las conexiones deben de poderse identificar de manera unívoca. Por tanto a cada conexión se le dará como identificador una cadena de caracteres, que podría tener una carga semántica (esto es, es mejor poner como nombre de una conexión, por ejemplo, "con_mysql_bd1_1", que "1", a fin de poder identificar a qué nos referimos más tarde), aunque en el ejemplo expuesto no lo haga hecho (por una comprensión pedagógica mejor).

Si pensamos un poco más, podemos llegar a la conclusión (¡hay muchas y seguro que el lector llegará a más que yo!) que para poder administrar un conjunto de conexiones tendremos que implementar, como mínimo, los métodos de agregar una conexión, eliminar una conexión, devolver un objeto conexión y por último devolver una lista con los identificadores de conexión (quizás este último método no sea necesario para el mínimo funcionamiento, pero sí que es recomendable).

Funciones auxiliares: f_esvacio

Una función muy útil que se utilizará en la implementación de esta clase es f_esvacio(cadena), que devuelve cierto si cadena es un objeto vacío (None ó que no contiene caracteres), y falso en caso contrario. La implementación es la siguiente:

def f_esvacio(cadena):
if cadena is None: return True
try:
if len(cadena) == 0: return True
except:
pass
if len(str(cadena)) == 0: return True
return False

Como se puede comprobar, el funcionamiento es casi trivial.

Implementación de la clase conexion

# Clase de gestión de conexiones.
class conexion(object):
def __init__(self):
self.__lista_conexiones = []

def agregar_conexion(self, nombre_conexion, objeto_conexion):
if f_esvacio(nombre_conexion): return False
if f_esvacio(objeto_conexion): return False
for i in self.__lista_conexiones:
if str(i[0]).strip() == str(nombre_conexion).strip():
return False
# Agregamos conexión.
self.__lista_conexiones.append((nombre_conexion,\
objeto_conexion))
# Nos vamos.
return True

def eliminar_conexion(self, nombre_conexion):
if f_esvacio(nombre_conexion): return False
for i in self.__lista_conexiones:
if str(i[0]).strip() == str(nombre_conexion).strip():
self.__lista_conexiones.remove((i[0],i[1]))
# Nos vamos.
return True
# Nos vamos.
return False

def listar_conexiones(self):
aux = []
for i in self.__lista_conexiones: aux.append(i[0])
return aux

def devolver_conexion(self, nombre_conexion):
if f_esvacio(nombre_conexion): return False
for i in self.__lista_conexiones:
if str(i[0]).strip() == str(nombre_conexion).strip():
return i[1]
# Nos vamos.
return False

Como se ha comentado anteriormente, esta clase tiene un único atributo (en el __init__) oculto, __lista_conexiones, que contendrá una lista de tuplas (nombre_conexion, objeto_conexion). El método agregar_conexion es bastante restrictivo y solo admite agregar una conexión si el identificador de conexión es válido (contiene caracteres), existe el objeto conexión, y dicho objeto conexión no tiene un identificador igual a otro existente en el atributo __lista_conexiones. El método eliminar_conexion elimina una conexión a partir del identificador de conexión pasado como parámetro. El método listar_conexiones devuelve una lista con los identificadores de conexión de la estructura. El método devolver_conexion devuelve el objeto conexión a partir del identificador de conexión pasado como parámetro. Si por cualquier razón el identificador de conexión no existiese, se devuelve falso.

En el siguiente post veremos la implementación de una clase que utiliza la clase conexion para gestionar múltiples conexiones con bases de datos, así como un ejemplo completo de uso.

Saludos.

lunes, 23 de agosto de 2010

¿Desaparecerá Python?

Hola. Navegando esta noche he encontrado un blog de gente pythónica, con una pinta muy buena, El blog oficial de APSL. Me ha llamado la atención su último artículo sobre la posible desaparición de Python como lenguaje de programación, en decremento de otros, como Java ó PHP. Sencillamente genial me ha parecido tanto los argumentos en defensa como los ejemplos de su utilización. Quizás falte un análisis técnico más riguroso de las bondades del framework Python, pero no hay nada que reprochar al artículo, ya que tampoco es cuestión de escribir un manuscrito. Me ha parecido excelente, al igual que el blog.

El enlace es el siguiente:

http://blog.apsl.net/2010/07/29/va-a-desaparecer-python/

Saludos.

sábado, 7 de agosto de 2010

Recursos documentales en la red, y 3.

Hola. Después de un paréntesis obligado vuelvo a escribir en El Viaje del Navegante. En este post presento una serie de enlaces sobre lugares que me han parecido interesantes, y que de vez en cuando visito. Como siempre, hago una partición temática de los mismos.

PYTHON

Python Links: Conjunto de links sobre programación en Python.

SQLAlchemy: Página oficial del ORM más potente de Python. Muy buen producto.

ActiveState Code Recipes: Recetas de problemas resueltos en Python. En inglés, pero muy buen sitio.

Python entre todos nº 1: El primer número de la nueva revista sobre Python de los chicos de PyAr. ¡¡¡Excelente iniciativa!!! Recomendado.

Página personal de Stephen Ferg: Sitio personal del señor Stephen Ferg. Está en inglés. Sus artículos son de lo más interesantes. Da gusto leer este tipo de artículos.

Introducción a Tkinter: Manual del framework gráfico Tkinter, que viene de serie en las distribuciones Python. está en inglés. Muy completo.

SOFTWARE LIBRE

WEBI: Directorio de software libre y código abierto.

Software libre: Información sobre software libre y nuevas tecnologías.

GNU España: Sitio web del Proyecto GNU y el movimiento del software libre. Abundante información.

Código abierto de IBM: Área de documentación de código abierto (opensource) de la gente de IBM. Podemos encontrar artículos de todo tipo, y por supuesto, de Python.

AppServ Open Project: Sitio web en donde se puede descargar e instalar de forma fácil Apache, PHP, MySQL en 1 minuto. Se evita el problema con la instalación de Apache, PHP, y MySQL. AppServ es completo pack de Apache, MySQL, PHP, phpMyAdmin. Lo que utilizo normalmente cuando quiero instalar todo el kit para desarrollo web y servidor MySQL. Muy bueno y sencillo de usar.

Calendario de eventos abiertos: Calendario hispano de eventos abiertos (conocimiento libre, soft y hard libre, etc).

edulibre.info: Foro sobre TICs con Software Libre en la educación aragonesa.

ASOLIF: Página web de la federación nacional de empresas de software libre ASOLIF (Asociaciones de Software Libre Federadas) .

CENATIC: Sitio web del Centro Nacional de Referencia de Aplicación de las TIC basadas en fuentes abiertas.

WEB

Django: Sitio web del proyecto Django, uno de los frameworks de desarrollo web basado en Python más potentes y rápidos del mercado, basado en el patrón Modelo-Vista-Controlador. Excelente.

Libros web: Web que publica gratuitamente traducciones de libros gratuitos relacionados con el diseño y la programación web, tales como CSS, Symfony, AJAX, JavaScript, etc. Todo en español. Recomendado.

ACTUALIDAD

Un blog en red: Sitio web sobre actualidad informática.

ReadWriteWeb España: Más actualidad sobre informática, tecnología y la sociedad de la información.

PORTALTIC: Noticias sobre tecnología.

SINC: Servicio de información y noticias científicas.

La pastilla roja: Sitio web con artículos muy interesantes sobre informática y sucedáneos.

SEGURIDAD

Kriptópolis: Sitio web sobre criptografía, privacidad y seguridad en Internet.

MISCELÁNEA

Planeta libro: Sitio web en donde se pueden encontrar cientos de libros gratis para descargar de dominio público. Todos en español.

Live football streaming: Sitio web en donde ver fútbol mundial on line, de forma gratuita.

Música clásica: Catálogo de ficheros de música clásica para descargar, en formato MP3. Mozart, Ravel, etc. Muy bueno.

PageRank: PageRank es un valor numérico que representa la importancia que una página web tiene en Internet. Google se hace la idea de que cuando una página coloca un enlace (link) a otra, es de hecho un voto para esta última. Este sitio web calcula cuanto importante es tu página web en internet.

Espero sean de vuestro agrado estos links.

Saludos.