Bastos

let’s code!


SQLObject e banco de dados legado

SqlObject tem um excelente suporte a banco de dados legado, e é o que preciso. Estou migrando umas aplicações para Python, elas usam Firebird e vão continuar usando. Problema: Além de um banco com nomes não elegantes para usar em objetos, tem hora que se usa generators para gerar IDs, outra hora usa-se procedures e por ai vai! Infelimente o Sqlobject OBRIGA a você ter um generator com o nome GEN_NOME_DA_TABELA. E eu preciso de outra maneira, então meti a mão na massa e alterei junto com um amigo a bibliteca, está aqui o Patch (Obviamente serviu para o meu caso, no seu eu não garanto!):

diff C3 C:/Python25/Lib/site-packages/SQLObject-0.9.1-py2.5.egg/sqlobject/firebird/firebirdconnection.py C:/Python25/Lib/site-packages/SQLObject-0.9.1-py2.5.egg/sqlobject/firebird/firebirdconnection_original.py
*** C:/Python25/Lib/site-packages/SQLObject-0.9.1-py2.5.egg/sqlobject/firebird/firebirdconnection.py	Fri Aug 24 15:59:04 2007
--- C:/Python25/Lib/site-packages/SQLObject-0.9.1-py2.5.egg/sqlobject/firebird/firebirdconnection_original.py	Thu Aug 16 10:39:08 2007
***************
*** 93,110 ****
          sequenceName = soInstance.sqlmeta.idSequence or \
                                 'GEN_%s' % table
          c = conn.cursor()
!         if id is None and soInstance.sqlmeta.idSequence is not None:
              c.execute('SELECT gen_id(%s,1) FROM rdb$database'
                                  % sequenceName)
              id = c.fetchone()[0]
!
!         if id is not None:
!             names = [idName] + names
!             values = [id] + values
!         else:
!             c.execute("SELECT cast(Max(%s) as integer) FROM %s WHERE 1=1" % (idName, table))
!             id = int(c.fetchone()[0])+1
!
          q = self._insertSQL(table, names, values)
          if self.debug:
              self.printDebug(conn, q, 'QueryIns')
--- 93,104 ----
          sequenceName = soInstance.sqlmeta.idSequence or \
                                 'GEN_%s' % table
          c = conn.cursor()
!         if id is None:
              c.execute('SELECT gen_id(%s,1) FROM rdb$database'
                                  % sequenceName)
              id = c.fetchone()[0]
!         names = [idName] + names
!         values = [id] + values
          q = self._insertSQL(table, names, values)
          if self.debug:
              self.printDebug(conn, q, 'QueryIns')

*Como se pode ver to usando windows para desenvolvimento, infelizmente… Solucionado o problema, agora vamos ver como conseguimos objetos elegantes com banco de dados nem tão elegantes assim. Entenda por não elegante um campo de uma tabela que se chama MENSAGENS, se chamar MSG_BODY ao invés de só BODY. Eu não gosto de criar siglas em campos.

# -*- coding: iso-8859-1-*-
from sqlobject import *
from pylons.database import PackageHub
import datetime

hub = PackageHub("XX_pylons")
__connection__ = hub

class Mensagem(SQLObject):
    class sqlmeta:
        table = "XX_MENSAGENS"
        idName = "MSG_CODIGO"
    subject = UnicodeCol(dbName='MSG_SUBJECT', dbEncoding="iso-8859-1", default='')
    body = UnicodeCol(dbName='MSG_BODY', dbEncoding="iso-8859-1", default='')
    status = IntCol(dbName='MSG_STATUS',default=0)
    data = DateTimeCol(dbName='MSG_DATA',default=datetime.datetime.now())
    para = ForeignKey('Usuario', dbName='USR_CODIGO_TO')
    de = ForeignKey('Usuario', dbName='USR_CODIGO_FROM')

Como podemos ver eu setei o nome das colunas no banco que correspondem com meus atributos do objeto. Então o campo que se chama no banco USR_CODIGO_TO no objeto se chama para.
Bem mais elegante. Eu também falei que aquele objeto no banco corresponde a tabela XX_MENSAGENS e o campo que é ID é o MSG_CODIGO. Caso você use generators pode colocar o campo idSequence para o nome do seu generator (ele vai concatenar GEN_ antes no nome que você passar). Você pode ver mais sobre isso na página de documentação do SQLObject. Que está meio desatualizada então vá ao TRAC deles também. Embora eles ainda indiquem a maneira antiga de se trabalar com banco de dados legado. Essas dicas que passei servem tmbém para qualquer outro banco, inclusive Mysql.

Para finalizar eu queria pedir encarecidamente que os DBAs por ai usem generators e com nomes padronizados!

Published by Bastos, on August 24th, 2007 at 4:52 pm. Filled under: patch, pylons, python, sqlobject | 2 Comments

Pylons, Firebird, problemas (e soluções)

Acho que vários programadores já tiveram problemas com codificações, utf-8, iso, latin… Bem, eu tinha um grande, o banco que estou trabalhando é um Firebird , latin-1 e o Sqlobject + mako + Pylons não se entenderam muito bem! Solução: Toda a plicação está utf-8:

# -*- coding: utf-8 -*-

E o model está

# -*- coding: iso-8859-1-*-

O SqlObject (vou falar depois sobre banco de dados legado) é muito legal e nos dá opções de encoding da coluna:

body = UnicodeCol(dbName='MSG_BODY', dbEncoding="iso-8859-1", default='')

O Pylons e o Mako permitem setar encode de inpurt e output (enviroment.py no load_environment):

    tmpl_options['mako.input_encoding'] = 'iso-8859-1'
    tmpl_options['mako.output_encoding'] = 'utf-8'
    tmpl_options['mako.default_filters'] = ['decode.utf8']
    request_settings = dict(charset='utf-8', errors='replace') 

    config['pylons.response_options']['charset'] = 'utf-8'

Assim tudo funcionou perfeitamente, 2 dias de pura loucura tentando fazer funcionar e agora parece estar tudo bem. Ponto para a documentação do Pylons e do Mako que, ambas, tem páginas tratando somente desse assunto.

Mudei minha mente sobre o Pylons agora que terminei uns testes, é um framework que cabe na cabeça e de fácil utilização, a documentação ainda perde para a do Django mas me foi bem mais útil que a do TurboGears, e como falou-se aqui, ele (TG) tem uma api instável (o bom é que vão mudar para o modelo do Pylons).

Published by Bastos, on August 24th, 2007 at 9:57 am. Filled under: firebird, pylons, python | 1 Comment

A melhor documentação é do Django! Sem dúvidas

Django tem uma API estável, uma linguagem de templates muito boa, suporte a vários bancos de dados, um ORM muito bom, e a melhor documentação de todos os frameworks escritos em Python. Tudo está documentado na documentação principal ou no wiki, e se não tiver use os grupos, muitas vezes os próprios desenvolvedores respondem. Fui testar o Pylons, no primeiro tutorial que fui ver, bem, ele não existe, 404! Fui no turbogears, documentação bastante desorganizada, a única vantagem é o SQLObject que no meu caso é bem vido por ter suporte a Firebird, que preciso no trabalho. Então se não vai usar Firebird, use Django, apesar de existir um patch pra Django suportar Firebird, ainda não testei, mas quem sabe…

[update]
Estou testando o Pylons, dei mais uma chance depois de comparar ele com o Turbogears… Até agora tudo bem, até o problema com o encoding foi fácil de solucionar, adicionei:

# -*- coding: iso-8859-1 -*-

no topo de meus templates (mako). A documentação está melhor que a do TB, ao menos agora me parece… Faz apenas alguns minutos que estou usando ele. Tomara que seja minha solução definitiva Python+Firebird para web.
[/update]

Published by Bastos, on August 21st, 2007 at 9:40 am. Filled under: django, pylons | 6 Comments