twisted adbapi: nomes de colunas em query

Quem já usou o twisted.enterprise.adbapi deve ter notado a falta de uma funcionalidade muitas vezes necessária na execução de queries (SELECT) no banco, seja ele qual for: colocar o nome das colunas no resultado, de forma que cada linha seja um dicionário e não um simples set.

Considerando que o adbapi é apenas um wrapper do Python Database API v2.0 (PEP-249), obviamente existem motivos pra essa funcionalidade não estar lá (além da preguiça de alguns). No documento, o primeiro item do FAQ:

Question: 

       How can I construct a dictionary out of the tuples returned by
       .fetch*():

    Answer:

       There are several existing tools available which provide
       helpers for this task. Most of them use the approach of using
       the column names defined in the cursor attribute .description
       as basis for the keys in the row dictionary.

       Note that the reason for not extending the DB API specification
       to also support dictionary return values for the .fetch*()
       methods is that this approach has several drawbacks:

       * Some databases don't support case-sensitive column names or
         auto-convert them to all lowercase or all uppercase
         characters.

       * Columns in the result set which are generated by the query
         (e.g.  using SQL functions) don't map to table column names
         and databases usually generate names for these columns in a
         very database specific way.

       As a result, accessing the columns through dictionary keys
       varies between databases and makes writing portable code
       impossible.

Na busca por uma solução, até encontrei um patch pro twisted, que adiciona um método runQueryMapped e retorna uma lista de dicionários, como eu queria. Porém, aplicar patch no twisted é furada, pois o código só funcionaria nas máquinas cujo twisted tem o tal patch. Fora de cogitação.

A solução mais simples (e tosca) que encontrei foi a que funcionou melhor:

# coding: utf-8
# hack for twisted.enterprise.adbapi.ConnectionPool class, providing
# a new method mapQuery (just like runQuery) whose return value
# is a list of dictionaries that map column names to values.

from twisted.enterprise import adbapi

class hackPool(adbapi.ConnectionPool):
    def _mapQuery(self, trans, *args, **kw):
        trans.execute(*args, **kw)
        rs, new_rs = trans.fetchall(), []
        names = [d[0] for d in trans.description]
        for values in rs:
            row = dict()
            for k, v in zip(names, values):
                row[k] = v
            new_rs.append(row)
        return new_rs

    def mapQuery(self, *args, **kw):
        return self.runInteraction(self._mapQuery, *args, **kw)

Dessa maneira, o hack fica no próprio código e não requer nenhum patch. Dá-lhe gambi.



Deixe um comentário