Convertir el objeto fila sqlalchemy a dict de python

¿Hay una forma sencilla de iterar sobre pares de nombre y valor de columna?

Mi versión de sqlalchemy es 0.5.6

Aquí está el código de ejemplo donde intenté usar dict (fila), pero arroja una excepción, TypeError: el objeto ‘Usuario’ no es iterable

import sqlalchemy from sqlalchemy import * from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker print "sqlalchemy version:",sqlalchemy.__version__ engine = create_engine('sqlite:///:memory:', echo=False) metadata = MetaData() users_table = Table('users', metadata, Column('id', Integer, primary_key=True), Column('name', String), ) metadata.create_all(engine) class User(declarative_base()): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) def __init__(self, name): self.name = name Session = sessionmaker(bind=engine) session = Session() user1 = User("anurag") session.add(user1) session.commit() # uncommenting next line throws exception 'TypeError: 'User' object is not iterable' #print dict(user1) # this one also throws 'TypeError: 'User' object is not iterable' for u in session.query(User).all(): print dict(u) 

Ejecutando este código en las salidas de mi sistema:

 sqlalchemy version: 0.5.6 Traceback (most recent call last): File "untitled-1.py", line 37, in  print dict(u) TypeError: 'User' object is not iterable 

Puede acceder al __dict__ interno de un objeto SQLAlchemy, como lo siguiente:

 for u in session.query(User).all(): print u.__dict__ 

No pude obtener una buena respuesta, así que uso esto:

 def row2dict(row): d = {} for column in row.__table__.columns: d[column.name] = str(getattr(row, column.name)) return d 

Edición: si la función anterior es demasiado larga y no es adecuada para algunos gustos, aquí hay una sola línea (python 2.7+)

 row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns} 

En SQLAlchemy v0.8 y posteriores, use el sistema de inspección .

 from sqlalchemy import inspect def object_as_dict(obj): return {c.key: getattr(obj, c.key) for c in inspect(obj).mapper.column_attrs} user = session.query(User).first() d = object_as_dict(user) 

Tenga en cuenta que .key es el nombre del atributo, que puede ser diferente del nombre de la columna, por ejemplo, en el siguiente caso:

 class_ = Column('class', Text) 

Este método también funciona para column_property .

 for row in resultproxy: row_as_dict = dict(row) 

las filas tienen una función _asdict() que da un dict

 In [8]: r1 = db.session.query(Topic.name).first() In [9]: r1 Out[9]: (u'blah') In [10]: r1.name Out[10]: u'blah' In [11]: r1._asdict() Out[11]: {'name': u'blah'} 

como @balki mencionó:

El método _asdict() se puede usar si está consultando un campo específico porque se devuelve como KeyedTuple .

 In [1]: foo = db.session.query(Topic.name).first() In [2]: foo._asdict() Out[2]: {'name': u'blah'} 

Mientras que, si no especifica una columna, puede utilizar uno de los otros métodos propuestos, como el que proporciona @charlax. Tenga en cuenta que este método solo es válido para 2.7+.

 In [1]: foo = db.session.query(Topic).first() In [2]: {x.name: getattr(foo, x.name) for x in foo.__table__.columns} Out[2]: {'name': u'blah'} 

Pregunta antigua, pero dado que este es el primer resultado para “sqlalchemy row to dict” en Google, merece una mejor respuesta.

El objeto RowProxy que devuelve SqlAlchemy tiene el método items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Simplemente devuelve una lista de tuplas (clave, valor). Así que uno puede convertir una fila a dict utilizando lo siguiente:

En Python <= 2.6:

 rows = conn.execute(query) list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows] 

En Python> = 2.7:

 rows = conn.execute(query) list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows] 
 from sqlalchemy.orm import class_mapper def asdict(obj): return dict((col.name, getattr(obj, col.name)) for col in class_mapper(obj.__class__).mapped_table.c) 

Siguiendo la respuesta de @balki, desde SQLAlchemy 0.8 puede usar _asdict (), disponible para objetos KeyedTuple. Esto hace una respuesta bastante sencilla a la pregunta original. Simplemente, cambie en su ejemplo las dos últimas líneas (el bucle for) para esta:

 for u in session.query(User).all(): print u._asdict() 

Esto funciona porque en el código anterior u es un objeto de la clase de tipo KeyedTuple , ya que .all () devuelve una lista de KeyedTuple. Por lo tanto, tiene el método _asdict () , que bien te devuelve u como un diccionario.

WRT la respuesta por @STB: AFAIK, una vez que .all () devuelve es una lista de KeypedTuple. Por lo tanto, lo anterior funciona, ya sea si especifica una columna o no, siempre y cuando se trate del resultado de .all () aplicado a un objeto de consulta.

La expresión por la que está iterando se evalúa en la lista de objetos del modelo, no en filas. Así que lo siguiente es el uso correcto de ellos:

 for u in session.query(User).all(): print u.id, u.name 

¿Realmente necesitas convertirlos a dicts? Claro, hay muchas maneras, pero entonces no necesitas la parte ORM de SQLAlchemy:

 result = session.execute(User.__table__.select()) for row in result: print dict(row) 

Actualización : Eche un vistazo al módulo sqlalchemy.orm.attributes . Tiene un conjunto de funciones para trabajar con el estado del objeto, que pueden ser útiles para usted, especialmente instance_dict() .

He encontrado esta publicación porque estaba buscando una forma de convertir una fila de SQLAlchemy en un dict. Estoy usando SqlSoup … pero la respuesta fue construida por mí mismo, así que, si pudiera ayudar a alguien, aquí están mis dos centavos:

 a = db.execute('select * from acquisizioni_motes') b = a.fetchall() c = b[0] # and now, finally... dict(zip(c.keys(), c.values())) 

Suponiendo que las siguientes funciones se agregarán a la class User lo siguiente devolverá todos los pares clave-valor de todas las columnas:

 def columns_to_dict(self): dict_ = {} for key in self.__mapper__.c.keys(): dict_[key] = getattr(self, key) return dict_ 

a diferencia de las otras respuestas, se devuelven todos, pero solo los atributos del objeto, que son atributos de Column a nivel de clase del objeto. Por lo tanto, no se _sa_instance_state ni ningún otro atributo de SQLalchemy o que usted agregue al objeto. Referencia

EDITAR: Olvídese de decir que esto también funciona en columnas heredadas.

extensión hybrid_propery

Si también desea incluir atributos de hybrid_property , funcionará lo siguiente:

 from sqlalchemy import inspect from sqlalchemy.ext.hybrid import hybrid_property def publics_to_dict(self) -> {}: dict_ = {} for key in self.__mapper__.c.keys(): if not key.startswith('_'): dict_[key] = getattr(self, key) for key, prop in inspect(self.__class__).all_orm_descriptors.items(): if isinstance(prop, hybrid_property): dict_[key] = getattr(self, key) return dict_ 

Supongo que aquí marca Columnas con un principio _ para indicar que desea ocultarlas, ya sea porque accede al atributo por una hybrid_property o simplemente no quiere mostrarlas. Referencia

Tipp all_orm_descriptors también devuelve hybrid_method y AssociationProxy si también desea incluirlos.

Comentarios a otras respuestas

Cada respuesta (como 1 , 2 ) que basada en el atributo __dict__ simplemente devuelve todos los atributos del objeto. Esto podría ser mucho más atributos que desea. Como _sa_instance_state esto incluye _sa_instance_state o cualquier otro atributo que defina en este objeto.

Cada respuesta (como 1 , 2 ) que se basa en la función dict() solo funciona en los objetos de fila de SQLalchemy devueltos por session.execute() no en las clases que define para trabajar, como la class User de la pregunta.

La respuesta de resolución que se basa en la row.__table__.columns definitivamente no funcionará. row.__table__.columns contiene los nombres de columna de la base de datos SQL. Estos solo pueden ser iguales al nombre de los atributos del objeto python. Si no, obtienes un AttributeError . Para respuestas (como 1 , 2 ) basadas en class_mapper(obj.__class__).mapped_table.c es lo mismo.

Consulte la Respuesta de Alex Brasetvik , puede usar una línea de código para resolver el problema

 row_as_dict = [dict(row) for row in resultproxy] 

Bajo la sección de comentarios de la Respuesta de Alex Brasetvik, zzzeek, ​​el creador de SQLAlchemy, declaró que este es el “Método correcto” para el problema.

Podrías intentar hacerlo de esta manera.

 for u in session.query(User).all(): print(u._asdict()) 

Utiliza un método incorporado en el objeto de consulta que devuelve un objeto dictonario del objeto de consulta.

referencias: https://docs.sqlalchemy.org/en/latest/orm/query.html

Puedes convertir el objeto sqlalchemy a un diccionario como este y devolverlo como json / dictionary.

Funciones de ayuda:

 import json from collections import OrderedDict def asdict(self): result = OrderedDict() for key in self.__mapper__.c.keys(): if getattr(self, key) is not None: result[key] = str(getattr(self, key)) else: result[key] = getattr(self, key) return result def to_array(all_vendors): v = [ ven.asdict() for ven in all_vendors ] return json.dumps(v) 

Función del conductor:

 def all_products(): all_products = Products.query.all() return to_array(all_products) 

Los documentos ofrecen una solución muy simple: ResultRow._asdict()

 def to_array(rows): return [r._asdict() for r in rows] def query(): data = session.query(Table).all() return to_array(data) 

Así es como lo hace Elixir. El valor de esta solución es que permite recursivamente la representación de relaciones del diccionario.

 def to_dict(self, deep={}, exclude=[]): """Generate a JSON-style nested dict/list structure from an object.""" col_prop_names = [p.key for p in self.mapper.iterate_properties \ if isinstance(p, ColumnProperty)] data = dict([(name, getattr(self, name)) for name in col_prop_names if name not in exclude]) for rname, rdeep in deep.iteritems(): dbdata = getattr(self, rname) #FIXME: use attribute names (ie coltoprop) instead of column names fks = self.mapper.get_property(rname).remote_side exclude = [c.name for c in fks] if dbdata is None: data[rname] = None elif isinstance(dbdata, list): data[rname] = [o.to_dict(rdeep, exclude) for o in dbdata] else: data[rname] = dbdata.to_dict(rdeep, exclude) return data 

¡Con este código también puede agregar a su consulta “filtro” o “unirse” y este trabajo!

 query = session.query(User) def query_to_dict(query): def _create_dict(r): return {c.get('name'): getattr(r, c.get('name')) for c in query.column_descriptions} return [_create_dict(r) for r in query] 

Dos caminos:

1.

 for row in session.execute(session.query(User).statement): print(dict(row)) 

2.

 selected_columns = User.__table__.columns rows = session.query(User).with_entities(*selected_columns).all() for row in rows : print(row._asdict()) 
 class User(object): def to_dict(self): return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")]) 

Eso debería funcionar.

Tengo una variación en la respuesta de Marco Mariani, expresada como decoradora. La principal diferencia es que manejará listas de entidades, además de ignorar con seguridad algunos otros tipos de valores de retorno (lo cual es muy útil cuando se escriben pruebas utilizando simulacros):

 @decorator def to_dict(f, *args, **kwargs): result = f(*args, **kwargs) if is_iterable(result) and not is_dict(result): return map(asdict, result) return asdict(result) def asdict(obj): return dict((col.name, getattr(obj, col.name)) for col in class_mapper(obj.__class__).mapped_table.c) def is_dict(obj): return isinstance(obj, dict) def is_iterable(obj): return True if getattr(obj, '__iter__', False) else False 

Soy un progtwigdor de Python recién creado y tuve problemas para llegar a JSON con tablas Unidas. Usando la información de las respuestas aquí, construí una función para devolver resultados razonables a JSON, donde se incluyen los nombres de las tablas, evitando tener que hacer alias o tener campos en conflicto.

Simplemente pase el resultado de una consulta de sesión:

test = Session (). query (VMInfo, Customer) .join (Customer) .order_by (VMInfo.vm_name) .limit (50) .offset (10)

json = sqlAl2json (prueba)

 def sqlAl2json(self, result): arr = [] for rs in result.all(): proc = [] try: iterator = iter(rs) except TypeError: proc.append(rs) else: for t in rs: proc.append(t) dict = {} for p in proc: tname = type(p).__name__ for d in dir(p): if d.startswith('_') | d.startswith('metadata'): pass else: key = '%s_%s' %(tname, d) dict[key] = getattr(p, d) arr.append(dict) return json.dumps(arr) 

Si la columna de su tabla de modelos no es igual a la columna mysql.

como :

 class People: id: int = Column(name='id', type_=Integer, primary_key=True) createdTime: datetime = Column(name='create_time', type_=TIMESTAMP, nullable=False, server_default=text("CURRENT_TIMESTAMP"), default=func.now()) modifiedTime: datetime = Column(name='modify_time', type_=TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), default=func.now()) 

Necesitará usar:

  from sqlalchemy.orm import class_mapper def asDict(self): return {x.key: getattr(self, x.key, None) for x in class_mapper(Application).iterate_properties} 

Si usa esta forma, puede obtener modify_time y create_time, ambos son Ninguno

 {'id': 1, 'create_time': None, 'modify_time': None} def to_dict(self): return {c.name: getattr(self, c.name, None) for c in self.__table__.columns} 

Debido a que el nombre de los atributos de clase no es igual al del almacén de columnas en mysql

Devuelve el contenido de este: class: .KeyedTuple como un diccionario

 In [46]: result = aggregate_events[0] In [47]: type(result) Out[47]: sqlalchemy.util._collections.result In [48]: def to_dict(query_result=None): ...: cover_dict = {key: getattr(query_result, key) for key in query_result.keys()} ...: return cover_dict ...: ...: In [49]: to_dict(result) Out[49]: {'calculate_avg': None, 'calculate_max': None, 'calculate_min': None, 'calculate_sum': None, 'dataPointIntID': 6, 'data_avg': 10.0, 'data_max': 10.0, 'data_min': 10.0, 'data_sum': 60.0, 'deviceID': u'asas', 'productID': u'U7qUDa', 'tenantID': u'CvdQcYzUM'} 

Por el bien de todos y de mí mismo, aquí está cómo lo uso:

 def run_sql(conn_String): output_connection = engine.create_engine(conn_string, poolclass=NullPool).connect() rows = output_connection.execute('select * from db1.t1').fetchall() return [dict(row) for row in rows] 
 def to_dict(row): return {column.name: getattr(row, row.__mapper__.get_property_by_column(column).key) for column in row.__table__.columns} for u in session.query(User).all(): print(to_dict(u)) 

Esta función puede ayudar. No puedo encontrar una mejor solución para resolver el problema cuando el nombre de atributo es diferente a los nombres de columna.

En la mayoría de los escenarios, el nombre de la columna es adecuado para ellos. Pero tal vez escriba el código como sigue:

 class UserModel(BaseModel): user_id = Column("user_id", INT, primary_key=True) email = Column("user_email", STRING) 

el column.name “user_email” mientras que el nombre del campo es “email”, el column.name no pudo funcionar bien como antes.

sqlalchemy_base_model.py

también escribo la respuesta aquí

Una solución que también funciona con clases heredadas:

 from itertools import chain from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Mixin(object): def as_dict(self): tables = [base.__table__ for base in self.__class__.__bases__ if base not in [Base, Mixin]] tables.append(self.__table__) return {c.name: getattr(self, c.name) for c in chain.from_iterable([x.columns for x in tables])} 

Aquí hay una forma super simple de hacerlo.

 row2dict = lambda r: dict(r.items()) 

No tengo mucha experiencia con esto, pero lo siguiente parece funcionar para lo que estoy haciendo:

 dict(row) 

Esto parece demasiado simple (en comparación con las otras respuestas aquí). ¿Qué me estoy perdiendo?