ORM Python

lasizoillo lasizoillo en gmail.com
Sab Mayo 23 17:43:01 CEST 2009


El día 23 de mayo de 2009 15:53, "Héctor S. Ponce"
<hectorsponce en gmail.com> escribió:
> bueno, gracias por las respuestas y a decir verdad es mi primer programa
> y he quedado encantado con el lenguaje.
> es la primera vez que posteo y veo que es de mucha ayuda.
> estoy con todo de acuerdo (id en la tabla, lo del loader de la db, lo
> del fichero de configuración u otro metodo, etc.) son cosas que ya las
> había pensado y hasta probado, pero el objetivo del ejercicio era solo
> cargar la db.
>
> experimente que mapeando la clase al estilo de:
>
> for i in parser:
>  p = Padron.valuesFromList(i)
>  session.add(p)
> session.commit()
>
> es exageradamente lerdo en comparación a la opción de "saturar la
> memoria !!! (proceso con 250 MB en ram)" con:

Como bien indica Antonio el problema es que una clase mapeada es
exageradamente lenta comparada con un versión de inserción sobre la
tabla directamente (como tu lo hacias). Cargar todo en memoria no ta
va a ayudar en el redimiendo respecto a usar generadores.

>
> bulk=[dict(zip(*[l for l in (data, unicode(props))])) for props in lista]
>
> o
>
> lista=[[field[1:len(field)-1] for field in line.split('|')] \
>   for line in file('/home/hectorsponce/sis/python/padron/padron.txt')]
>

lista = ( (field[1, -1] for field in line.split('|')) for line in
file('filename') )

Prueba esa otra versión. En lista no estas guardando una lista
colosal, sino el equivalente a un cursor de base de datos (más o
menos). El caso es que puedes ir recorriendo lista para ir leyendo las
lineas una a una según las vas consumiendo.

> (aparte que queria generar las variables en una linea de codigo !!! increible!!!)
>
>
> tengo otras 3 consultas:
>
> 1) no entiemdo bien el tema del "yield"

http://pyspanishdoc.sourceforge.net/tut/node11.html#SECTION0011900000000000000000

Ahí tienes una breve introducción (leete el punto 9.10 tb). Más
adelante veras que puedes hacer mucha magia con ellos (todavía están
evolucionando la magia del yield en las nuevas versiones de python).

> 2) no le puse a la tabla un  campo id por no saber que valor pasarle de
> parametro al insertar una fila !!! como lo hago?

Si generas un id de tipo entero y que sea primary_key, sqlalchemy te
genera un campo entero auto-incremental. Cuando insertas, si no le
pasas valor al campo, el ya se encarga de coger el siguiente que le
toca.

> 3) aca va una clase que hice para parsear un archivo de texto con campos
> fijos en el que pueden venir varios tipos de registro (en el mismo
> archivo diferenciados por una cabecera, o diferentes archivos... como en
> el ejemplo). quisiera escuchar cualquier comentario.
>
>
> ################
>
> import sqlite3
>
> class fixed_text_record():
>    """
>    Clase para proceso de registros de texto fijo
>    Modo de uso:
>        r=record()
>        r.add_field_definition('motos','razon_social',0,69)
>        r.add_field_definition('motos','tipo_documento',70,74)
>        r.add_field_definition('autos','tipo_documento',70,74)
>        for line in file('mi_archivo'):
>            row = r.get_fields(line, 'motos')
>    """
El ejemlo de uso se puede transformar fácilmente en un doctest. Al
usar doctests puedes probar automáticamente que la documentación es
incorrecta porque refactorizaste la funcion record para que se llamara
fixed_text_record.
http://docs.python.org/library/doctest.html

He de confesar que no uso mucho eso del doctests, pero es una de esas
cosas pendientes que tengo en mente.

>    def __init__(self):
>        self.types={}
>
>    def add_field_definition(self, type, field, begin, end):
>        f={}
>        if not self.types.has_key(type):
>            self.types[type]=f
>        else:
>            f=self.types[type]
>        f[field]={'begin':begin, 'end':end}
>
>    def get_fields(self, record, type):
>        values={}
>        limits={}
>        fields=self.types[type]
>        for field in fields.keys():
>            limits=fields[field]
>            for limit in limits.keys():
>
> values[field]=record[limits['begin']:limits['end']].strip().replace("'","")
>        return values
>
>
Los parsers muchas veces son algo más dificiles de hacer. Si tu cadena
contiene a algo como "'1'   'James O'Brian'    'Albañil'".

Habras cambiado el apellido de ese tal O'Brian a OBrian.

>
>
> database_name = "registro.db"
> db_connection = sqlite3.connect(database_name)
> db_cursor = db_connection.cursor()
>
> cuantos=0
> r=fixed_text_record()
>
> """ Definicion del registro archivo de motos"""
> r.add_field_definition('motos','razon_social',0,69)
> r.add_field_definition('motos','tipo_documento',70,74)
> r.add_field_definition('motos','numero_documento',75,89)
> r.add_field_definition('motos','cuit',90,104)
> r.add_field_definition('motos','nueva_numero',105,109)
> r.add_field_definition('motos','nueva_letra',110,114)
> r.add_field_definition('motos','fabricacion',115,120)
> r.add_field_definition('motos','marca',121,180)
> r.add_field_definition('motos','cilindrada',181,185)
> r.add_field_definition('motos','domicilio',187,441)
> r.add_field_definition('motos','localidad',442,501)
> r.add_field_definition('motos','provincia',502,531)
> r.add_field_definition('motos','postal',532,541)
> r.add_field_definition('motos','propietario',542,547)
>
> """ Definicion del registro archivo de automotores"""
> r.add_field_definition('autos','razon_social',0,69)
> r.add_field_definition('autos','tipo_documento',70,74)
> r.add_field_definition('autos','numero_documento',75,89)
> r.add_field_definition('autos','cuit',90,104)
> r.add_field_definition('autos','nueva_letra',105,109)
> r.add_field_definition('autos','nueva_numero',110,114)
> r.add_field_definition('autos','vieja_letra',115,117)
> r.add_field_definition('autos','vieja_numero',118,127)
> r.add_field_definition('autos','fabricacion',128,133)
> r.add_field_definition('autos','marca',134,193)
> r.add_field_definition('autos','cilindrada',194,253)
> r.add_field_definition('autos','domicilio',254,508)
> r.add_field_definition('autos','localidad',509,568)
> r.add_field_definition('autos','provincia',569,598)
> r.add_field_definition('autos','postal',599,608)
> r.add_field_definition('autos','propietario',609,613)
>
> db_cursor.execute('delete from registro')
> db_connection.commit()
>
> """ Proceso Motos.txt"""
> for line in file('Motos.txt'):
>    cuantos+=1
>    row = r.get_fields(line, 'motos')
>    tipo_documento=''
>    if row["tipo_documento"] == 'U': tipo_documento='DNI'
>    elif row["tipo_documento"] == 'C': tipo_documento='LC'
>    elif row["tipo_documento"] == 'E': tipo_documento='LE'
>    elif row["tipo_documento"] == 'I': tipo_documento='CI'
>    else: tipo_documento=row["tipo_documento"]

Con lo que te gusta ser conciso creo que este par de lineas te va a gustar ;-)

t_documentos = dict(U="DNI", C="LC", E="LE", I="CI")
tipo_documento = t_documentos.get(row["tipo_documento"], row["tipo_documento"])

Aparte se puede generar el diccionario que define las equivalencias en
un fichero de configuración.

>
>    propietario=''
>    if row["propietario"] == 'R': propietario='RESP. FISCAL'
>    elif row["propietario"] == 'T': propietario='TITULAR'
>    else: propietario=row["propietario"]
>
>    sql="insert into registro ("
>    sql=sql+"razon_social, tipo_documento, numero_documento, "
>    sql=sql+"cuit, nueva, fabricacion, marca, cilindrada, domicilio, "
>    sql=sql+"localidad, provincia, postal, propietario, tipo) "
>    sql=sql+"values ('"+row["razon_social"]+"', '"+tipo_documento+"', '"
>    sql=sql+row["numero_documento"]+"', '"+row["cuit"]+"',
> '"+row["nueva_numero"]+row["nueva_letra"]+"', '"
>    sql=sql+row["fabricacion"]+"', '"+row["marca"]+"',
> '"+row["cilindrada"]+"', '"
>    sql=sql+row["domicilio"]+"', '"+row["localidad"]+"',
> '"+row["provincia"]+"', '"
>    sql=sql+row["postal"]+"', '"+propietario+"', 'MOTO')"
>

Expero que te puedas fiar del fichero de entrada. Este codigo admite
toda clase de inyección SQL.

>    db_cursor.execute(sql)
>
> db_connection.commit()
>
> print str(cuantos) + ' registros de motos insertados.'
>

Un saludo:

Javi
_______________________________________________
Lista de correo Python-es 
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes





Más información sobre la lista de distribución Python-es