sábado, agosto 04, 2007

SQLObject: Guardando Objetos en Una Base de Datos fácilmente.

SQLObject es un Mapeador de Objetos en Tablas de Bases de Datos, Object Relational Mapper o Object Relational Manager(o simplement ORMs). Si bien existen otros ORMs, como SQLAlchemy o el propio ORM de Django, SQLObject se caracteriza por su curva de aprendizaje de escasa pendiente.
Las bases de datos
Las Bases de Datos actuales se basan en el concepto de entidades (persona, auto, ejemplar) y relaciones (tiene, maneja, pertenece -Generalmente estos nombres se suelen usar en las etapas más primigenias de los modelos-).
Inicialmente se crea un modelo llamado conceptual, donde se utilizan 3 primitivas: especificación, generalización y agregación. Como las bases de datos no soportan el concepto de herencia (generalización), entre otras cosas, se va "bajando" el modelo, hacia un modelo lógico, que si es posible utilizar en una base de datos.
Para interactuar con estas bases de datos relacionales, se utiliza un lenguaje llamado SQL (Structured Query Language), que tiene su estandar y todo, pero como siempre, hay varios sabores.
Los comandos SQL se dividen en DDL y DML. DDL (Data Definition Language) es el lenguaje que nos permite generar y alterar la estructura de la base de datos. DML es el lenguaje de manipulación de datos, con el que podemos ingresar datos (INSERT), recuperar (SELECT), borrar (DELETE) y actualizar (UPDATE).
Dentro del software que usamos para las bases de datos tenemos: DBDesigner, Open System Architect (version Open Source de Data Architect), PGAdmin 3, MySQL Administrator, MySQL Query Browser, SQLiteman y SQLite Browser.
Los objetos
Los objetos se basan en diferentes principios de los que rigen las bases de datos (herencia, polimorfismo, encapsulamiento, persistencia, etc.). En un mundo ideal, con infinita memoria persistente, no necesitaríamos guardar objetos en una base de datos, pero, en el mundo real, debemos hacerlo.
Si pensamos en que es un objeto, en Python, vendrá a nuestras cabezas la idea del diccionario, de métodos(funciones) y atributos(variables).
En python, los atributos de instancia, son inicializados en el inicializador (o método __init__), de manera que de antemano, no podemos "ver" los atributos de la clase, así que de alguna manera tendremos que especificarle al ORM, que datos queremos guardar de nuestra clase.
También será necesario saber que tipo de datos queremos guardar de cada objeto. Un nombre será de tipo cadena, una fecha de tipo fecha, un precio de tipo flotante, y así con cada elemento de nuestro modelo de clases.
Acción
Bien, vamos que como se plasma toda esta cachara en código Python. Para esto voy a suponer que tienen SQLite instalado y SQLObject (con easy_install de las python-setuptool se instala perfectametne)
from sqlobject import *
# Esta es una base de datos en memoria
__connection__ = "sqlite:/:memory:"
class Persona(SQLObject):
nombre = StringCol(length=30)
apellido = StringCol(length=30)
altura = FloatCol()
Persona.createTable()
# Creamos tres personas, no hace falta que guardemos las instancias en p
# ya que no se las lleva el GC, están en la DB!!!
p = Persona(nombre = "Eduardeo", apellido = "Perez", altura = 1.96)
p = Persona(nombre = "Maria Laura", apellido = "Garatti", altura = 1.73)
p = Persona(nombre = "Pablo", apellido = "Martinez", altura = 1.68)

# Ahora recorremos las persona que generamos
for i in Persona.select():
print i
# Y ahora una connsulta con altura, pasando a condición SQL
for i in Persona.select("""altura > 1.70 """):
print i
Relaciones
Las relaciones entre objetos, pueden ser uno a uno, uno a muchos o muchos a muchos. En este aso el ejemplo es sencillo. Es una relación uno a muchos.
El ejemplo se trata de un modelo donde tenemos proveedores y productos.

#! /usr/bin/python

from sqlobject import *

# Para usar MySQL
# mysql:/usuario:pass@host/base

__connection__ = "sqlite:/:memory:"

class Proveedor(SQLObject):
nombre = StringCol(length=20)
direccion = StringCol(length=60)
def __str__(self):
return self.nombre

class Producto(SQLObject):
nombre = StringCol(length=20)
precio = FloatCol()
proveedor = ForeignKey('Proveedor')

def __str__(self):
return "Prod: %s $ %.2f" % (self.nombre, self.precio)

# El programa principal
if __name__ == "__main__":
for tabla in [ Proveedor, Producto]:
tabla.createTable()

p1 = Proveedor(nombre = "Muebles del Sur", direccion="Av. Alcorta Cicatriz")
p2 = Proveedor(nombre = "Muebles del Este", direccion="Zapiola 340")
p3 = Proveedor(nombre = "Equipamientos Fontana", direccion="Fontana 223")
# Para achicar un poco el codigo y usar algo de los hermosos tipo
# que python nos provee, armamos un diccionario con el proveedor
# como clave y una lista de tuplas (producto, precio)
datos_pro = { p1 : [('Silla', 120), ('Mesa', 150), ('alacena',200)],
p2 : [('Armario', 300), ('Frazada', 70),
('Pytho For Dummies', 9.99) ],
p3 : [('Martillo',25), ('Neumatico',200), ('Banco', 70)]

}
for prov, productos in datos_pro.iteritems():
for nombre, precio in productos:
Producto( nombre = nombre, precio = precio, proveedor = prov)
print "-"*40
print "Mostrando todos los productos:"
print "-"*40
for prod in Producto.select():
print prod
print "-"*40
# Un conjunto (set) tiene la propiedad de que sus elementos no se repiten
# a diferencia de una lista o un diccionario.
provs = set()

for p in Producto.select("""precio < 100"""):
provs.add(p.proveedor)
print "-"*40
print "Proveedores con productos de menos de $100:"
print "-"*40
for p in provs:
print "*", p
A diferencia de PyQt, donde es necesario mantener la referencia al objeto una vez instanciado para que el Garbage Collector no nos lo borre, en este caso, con cada instancia que generamos se genera una tupla en la base de datos. De manera que no es necesario guardar estas referencias. El método select(), que heredamos de las clase SQLObject, nos permite hacer consultas. Le pasamos como argumento, lo que podríamos en el WHERE de nuestro SQL.

Si bien es necesario saber algo de SQL para sacarle el jugo, es evidente las facilidad con la que persistimos objetos.
SQLObject tiene muchas cosas interesantes, de hecho, es el ORM de TurboGears, un framework web ala Rails.

1 comentario:

stormbringer dijo...

Gracias, no conocía SQLObject.

Me va a facilitar mucho lo que estoy haciendo.