python - query - Convertir el objeto de fila sqlalchemy en pitón dict
sqlalchemy select (25)
¿Hay una manera simple de iterar sobre el nombre de la columna y los pares de valores?
Mi versión de sqlalchemy es 0.5.6
Aquí está el código de muestra donde intenté usar dict (row), pero arroja una excepción, TypeError: el objeto ''User'' 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 <module>
print dict(u)
TypeError: ''User'' object is not iterable
Esta es la respuesta correcta para la moderna SQLAlchemy (v0.8 - v1.2 +).
Usa 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
.
Actualización 2018-03-25:
Ya no puede usar u.__dict__
, ya que lo interpreta como una referencia a una columna / campo. En su lugar use dict(u)
.
Respuesta Original:
Puede acceder al __dict__
interno de un objeto SQLAlchemy, como el siguiente ::
for u in session.query(User).all():
print u.__dict__
Aquí hay una forma muy simple de hacerlo
row2dict = lambda r: dict(r.items())
Así es como lo hace Elixir. El valor de esta solución es que permite recursivamente incluir la representación del diccionario de relaciones.
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
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]
En la sección de comentarios de la respuesta de Alex Brasetvik, zzzeek, el creador de SQLAlchemy, afirmó que este es el "Método correcto" para el problema.
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())
En la mayoría de los escenarios, el nombre de la columna es adecuado para ellos. Pero tal vez usted escriba el código de la siguiente manera:
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 podría funcionar bien como antes.
Encontré 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, por lo tanto, si pudiera ayudar a alguien, aquí está mi granito de arena:
a = db.execute(''select * from acquisizioni_motes'')
b = a.fetchall()
c = b[0]
# and now, finally...
dict(zip(c.keys(), c.values()))
La expresión por la que está iterando se evalúa como una lista de objetos modelo, no filas. Entonces el 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 luego no necesitas ORM como parte 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()
.
My take utiliza (too many?) Diccionarios:
def serialize(_query):
#d = dictionary written to per row
#D = dictionary d is written to each time, then reset
#Master = dictionary of dictionaries; the id Key (int, unique from database) from D is used as the Key for the dictionary D entry in Master
Master = {}
D = {}
x = 0
for u in _query:
d = u.__dict__
D = {}
for n in d.keys():
if n != ''_sa_instance_state'':
D[n] = d[n]
x = d[''id'']
Master[x] = D
return Master
Ejecutar con matraz (incluido jsonify) y flask_sqlalchemy para imprimir salidas como JSON.
Llame a la función con jsonify (serialize ()).
Funciona con todas las consultas de SQLAlchemy que he intentado hasta ahora (ejecutando SQLite3)
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
Editar: si la función anterior es demasiado larga y no es adecuada para algunos gustos, aquí hay un trazador de líneas (python 2.7+)
row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
No tengo mucha experiencia con esto, pero lo siguiente parece funcionar para lo que estoy haciendo:
dict(row)
Esto parece demasiado simple (comparado con las otras respuestas aquí). ¿Qué me estoy perdiendo?
Puede 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)
Siguiendo la respuesta de @balki, desde SQLAlchemy 0.8 puede usar _asdict (), disponible para objetos KeyedTuple. Esto brinda una respuesta bastante directa a la pregunta original. Solo, cambie en su ejemplo las dos últimas líneas (el bucle for) para este:
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 muy bien te devuelve un diccionario.
WRT la respuesta por @STB: AFAIK, y eso devuelve .all () es una lista de KeypedTuple. Por lo tanto, lo anterior funciona bien si especifica una columna o no, siempre que se trate del resultado de .all () tal como se aplica a un objeto Query.
Soy un programador de Python recién acuñado y tuve problemas para llegar a JSON con tablas combinadas. Utilizando la información de las respuestas aquí construí una función para devolver resultados razonables a JSON, donde los nombres de las tablas están incluidos, evitando tener que alias, o que los campos colisionen.
Simplemente pase el resultado de una consulta de sesión:
test = Session (). query (VMInfo, Customer) .join (Cliente) .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)
Suponiendo que se agregarán las siguientes funciones al class User
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 los atributos del objeto, que son atributos de Column
en el nivel de clase del objeto. Por lo tanto, no se _sa_instance_state
ni ningún otro atributo SQLalchemy o se agrega al objeto. Reference
EDITAR: Olvida decir que esto también funciona en Columnas heredadas.
extensión de hybrid_propery
Si también desea incluir atributos hybrid_property
lo siguiente funcionará:
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 marca las Columnas con un comienzo _
para indicar que desea ocultarlas, ya sea porque accede al atributo por una hybrid_property
o simplemente no desea mostrarlas. Reference
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 se basa en el atributo __dict__
simplemente devuelve todos los atributos del objeto. Esto podría ser mucho más atributos de los deseados. Me gusta que 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 SQLalchemy fila objetos 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 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.
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 de manera segura otros tipos de valores devueltos (lo cual es muy útil cuando se escriben pruebas usando simulaciones):
@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
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])}
Una vieja pregunta, pero dado que este es el primer resultado para "sqlalchemy row to dict" en Google, merece una mejor respuesta.
El objeto RowProxy que SqlAlchemy devuelve 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). Entonces uno puede convertir una fila a dict usando 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]
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 usar uno de los otros métodos propuestos, como el proporcionado por @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''}
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''}
Con este código también puede agregar a su consulta "filtro" o "unirse" y ¡esto funciona!
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]
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.
for row in resultproxy:
row_as_dict = dict(row)
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)