Convertir objeto de fila sqlalchemy a python dict


241

¿Hay una manera simple 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 muestra 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)

Ejecutar 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

3
El título de la pregunta no coincide con la pregunta en sí. Según los documentos Las filas de resultados devueltas por Consulta que contienen varias entidades ORM y / o expresiones de columna hacen uso de esta clase para devolver filas. donde esta clase es sqlalchemy.util.KeyedTupleel objeto de fila del título de la pregunta. Sin embargo, la consulta en la pregunta usa la clase modelo (mapeada), por lo que el tipo de objeto de fila es la clase modelo en lugar de sqlalchemy.util.KeyedTuple.
Piotr Dobrogost

2
@PiotrDobrogost La pregunta es de 2009 y menciona la versión 0.5.6 de sqlalchemy
Anurag Uniyal

Respuestas:


233

Puede acceder al interno __dict__de un objeto SQLAlchemy, como el siguiente ::

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

24
La mejor respuesta en este hilo, no sé por qué todos los demás proponen soluciones mucho más complicadas.
Dave Rawks

92
Esto proporciona un campo adicional '_sa_instance_state', al menos en la versión 0.7.9.
elbear

21
así que esto sería mejor:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing

66
Esto no parece ideal ya que, como la gente ha señalado, __dict__incluye una _sa_instance_stateentrada que luego debe eliminarse. Si actualiza a una versión futura y se agregan otros atributos, es posible que tenga que regresar y tratarlos manualmente. si desea solo datos de columna (por ejemplo, para tomar una lista de instancias de una consulta y colocarlos en un marco de datos de pandas), entonces, {col.name: getattr(self, col.name) for col in self.__table__.columns}tal como respondió Anurag Uniyal (con correcciones importantes de los comentarios a esa respuesta) parece más sucinto y error- prueba.
kilgoretrout

14
Esta respuesta es incorrecta. La pregunta incluso tiene dict(u)y afirma correctamente que arroja a TypeError.
RazerM

146

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 una línea única (python 2.7+)

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

17
Más sucintamente, return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()).
argentpepper

44
@argentpepper, sí, incluso puedes hacerlo row2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())para que sea un trazador de líneas real, pero prefiero que mi código sea legible, horizontalmente corto, verticalmente largo
Anurag Uniyal

14
¿Qué sucede si mi columna no está asignada a un atributo del mismo nombre? IE, x = Column('y', Integer, primary_key=True)? Ninguna de estas soluciones funciona en este caso.
Botones840

13
drdaeman tiene razón, aquí está el fragmento correcto:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax

55
Esta respuesta hace una suposición no válida: los nombres de columna no necesariamente coinciden con los nombres de los atributos.
RazerM

95

Según @zzzeek en los comentarios:

tenga en cuenta que esta es la respuesta correcta para las versiones modernas de SQLAlchemy, suponiendo que "fila" es un objeto de fila central, no una instancia asignada a ORM.

for row in resultproxy:
    row_as_dict = dict(row)

13
Dice 'El objeto XXX no es iterable', estoy usando 0.5.6, obtengo por session.query (Klass) .filter (). All ()
Anurag Uniyal

6060
tenga en cuenta que esta es la respuesta correcta para las versiones modernas de SQLAlchemy, suponiendo que "fila" es un objeto de fila central, no una instancia asignada a ORM.
zzzeek

48
También tenga en cuenta que zzzeek es el creador de sqlalchemy.
Chris

1
¿Alguien puede dar más detalles sobre este? Soy un novato y no entiendo esto.
lameei

1
¿Cuál es la diferencia entre un objeto de fila central frente a una instancia mapeada de ORM? Esto no funciona para mí en las filas de query(MyModel).all(): El objeto MyModel no es iterable.
Jonathan Hartley

81

En SQLAlchemy v0.8 y más reciente, 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 .keyes 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.


@DukeDougal Creo que esto funciona desde v0.8 (cuando se agregó el sistema de inspección).
RazerM

1
Esto funciona con Sqlalchemy v2.0. Otras respuestas no.
Thanh Nguyen

Esto no tiene en cuenta las columnas diferidas
Mark

1
@ Mark No está claro para mí que deberían excluirse por defecto. Sin embargo, puede verificar que las llaves no estén ensqlalchemy.inspect(obj).unloaded
RazerM

55
@NguyenThanh ¡Trabajar con SQLAlchemy v2.0 es particularmente impresionante dada su inexistencia! La última versión (beta) es v1.3.0b1.
Mark Amery

39

las filas tienen una _asdict()función 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'}

Se supone que es privado y no podría ser eliminado / cambiado en futuras versiones.
balki

2
@balki Está bastante bien documentado y, como tal, no del todo privado. Aunque un guión bajo importante tiene ese significado en Python en general, aquí probablemente se usa para no chocar con posibles teclas de tupla.
Ilja Everilä

55
Esto solo funciona con KeyedTuple s, que solo se devuelven al consultar campos específicos de una fila. es decir .query (Topic.name) devuelve un KeyedTuple, mientras que .query (Topic) devuelve un Topic, que no tiene ._asdict () - Derp. Acabo de ver la respuesta de STB a continuación.
Chad Lowe

20

como @balki mencionó:

El _asdict()método 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'}

Si los atributos de la clase ORM de python tienen nombres diferentes de las columnas de la base de datos, pruebe esta solución: stackoverflow.com/questions/27947294/…
TaiwanGrapefruitTea

2
en realidad, el autor de sqlalchemy proporciona una solución mejor para todos los casos en stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea

Cuando intento esto obtengo el objeto ResultProxy no tiene atributo '_asdict'
slashdottir

@slashdottir, ¿está ejecutando su objeto de consulta (llamando al .first()método)?
Sam Bourne

1
Esta respuesta hace una suposición inválida: los nombres de las columnas no necesariamente coinciden con los nombres de los atributos; vea la respuesta de RazerM.
Piotr Dobrogost

18

Antigua pregunta, 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). 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]

13
Puedes hacerlolist_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen, el

Un inconveniente es que los nombres de columna que SQLAlchemy usa en un conjunto de resultados son table_name_column_name, si desea nombres diferentes (por ejemplo, solo column_name), use el .labelmétodo. session.query( MyTable.column_name.label('column_name'), ... )
Aneel

Hola, estoy recibiendo este problema, por favor ayúdenme * datetime.datetime (2018, 11, 24, 18, 52, 50) no es serializable JSON *
Saravanan Nandhan

13

Asumiendo que las siguientes funciones se agregarán a las class Usersiguientes, se devolverán 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, excepto los atributos del Columnnivel de clase del objeto. Por lo tanto, no se incluye _sa_instance_stateningún atributo SQLalchemy o cualquier otro que agregue al objeto. Referencia

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

hybrid_propery extensión

Si también desea incluir hybrid_propertyatributos, 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 comienzo _para indicar que desea ocultarlas, ya sea porque accede al atributo mediante un hybrid_propertyo simplemente no desea mostrarlas. Referencia

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

Observaciones a otras respuestas

Cada respuesta (como 1 , 2 ) que se basa en el __dict__atributo simplemente devuelve todos los atributos del objeto. Esto podría ser mucho más atributos de lo que deseas. Como triste, esto incluye _sa_instance_stateo cualquier otro atributo que defina en este objeto.

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

La respuesta de resolución que se basa row.__table__.columnsdefinitivamente no funcionará. row.__table__.columnscontiene 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 las respuestas (como 1 , 2 ) basadas en class_mapper(obj.__class__).mapped_table.cesto es lo mismo.


12
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)

44
Tenga en cuenta la diferencia entre local_table y mapped_table. Por ejemplo, si aplica algún tipo de herencia de tabla en su base de datos (tbl_employees> tbl_managers, tbl_employees> tbl_staff), sus clases asignadas deberán reflejar esto (Gerente (Empleado), Personal (Empleado)). mapped_table.c le dará los nombres de columna de la tabla base y la tabla heredada. local_table solo le da el nombre de su tabla (heredada).
Michael Ekoka

Esto evita dar el campo '_sa_instance_state', al menos en la versión 0.8+.
Evan Siroky

44
Esta respuesta hace una suposición no válida: los nombres de columna no necesariamente coinciden con los nombres de los atributos.
RazerM

11

Siguiendo la respuesta de @balki, desde SQLAlchemy 0.8 puede usar _asdict (), disponible para objetos KeyedTuple. Esto genera una respuesta bastante directa 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 tipo clase KeyedTuple , ya que .all () devuelve una lista de KeyedTuple. Por lo tanto, tiene el método _asdict () , que devuelve muy bien u como diccionario.

Escribe la respuesta de @STB: AFAIK, y aunque .all () devuelva es una lista de KeypedTuple. Por lo tanto, lo anterior funciona si especifica una columna o no, siempre y cuando esté tratando con el resultado de .all () tal como se aplica a un objeto Query.


66
Esto puede haber sido cierto en el pasado, pero en SQLAlchemy v1.0 .all()devuelve una lista de Userinstancias, por lo que esto no funciona.
RazerM

@ RazerM, lo siento, pero no entiendo lo que quieres decir. El bucle debe bucle precisamente a través de la lista de instancias de usuario, ellos (u) convertir a los diccionarios, y luego imprimirlas ...
jgbarah

3
UserLas instancias no tienen un _asdictmétodo. Ver gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM

2
Ahora lo tengo. Gracias. En lugar de KeyedTuple, ahora .all () devuelve objetos de Usuario. Entonces, el problema para v1.0 (y en adelante, supongo) es cómo sacar un diccionario de un objeto Usuario. Gracias por la aclaración.
jgbarah

10

La expresión que está iterando evalúa la 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 dictos? Claro, hay muchas maneras, pero entonces no necesitas ORM parte de SQLAlchemy:

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

Actualización : Echa un vistazo al sqlalchemy.orm.attributesmódulo. Tiene un conjunto de funciones para trabajar con el estado del objeto, que puede ser útil para usted, especialmente instance_dict().


2
Quiero convertirlos a dict, porque algún otro código necesita datos como dict, y quiero una forma genérica porque no sabré qué columnas tiene un objeto modelo
Anurag Uniyal

y cuando tengo acceso a ellos, solo tengo acceso a los objetos del modelo, así que no puedo usar session.execute, etc.
Anurag Uniyal

8

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, declaró que este es el "Método correcto" para el problema.


1
@Greenonline Claro, el comentario de aprobación está bajo la respuesta de Alex Brasetvik. Editado para agregar enlace a su respuesta
NorWay

¿Cuál es el resultproxy?
lameei

8

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


1
¿Agregar más explicaciones tal vez?
Tiw

1
Nada más que explicar. Es un método incorporado en el objeto de resultado. Entonces, ya sea que haga esto para todos los resultados, o una sola fila, hay un _asdict()método incorporado que esencialmente comprime los nombres de campo con valores de campo y devuelve el resultado como un diccionario.
Mateo

Muy conciso y desearía que funcionara, pero uen mi caso es una cadena, y recibo el error `` El objeto modelo '' no tiene ningún atributo '_asdict' '@hllau a continuación funcionó para mí
Mote Zart

7

Encontré esta publicación porque estaba buscando una manera de convertir una fila SQLAlchemy en un dict. Estoy usando SqlSoup ... pero la respuesta fue creada 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()))

1
o, si lo prefiere ...: [dict (zip (i.keys (), i.values ​​())) para i en b]
Mychot triste el

¡Esta es la única sintaxis que he encontrado que realmente funciona! He estado probando cosas por más de una hora.
slashdottir

Para las selecciones principales, RowProxy( cen esta respuesta) se adhiere al protocolo de mapeo, por lo que puede llamar dict(c).
RazerM

4

Puede convertir el objeto sqlalchemy en 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)

3

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())

3

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)

2

Así es como lo hace Elixir. El valor de esta solución es que permite incluir recursivamente la representación de las relaciones en el 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

Link está muerto. La próxima vez, copie el código correspondiente aquí también.
Gus E

Lo haré la próxima vez. Si no recuerdo mal, la función fue bastante larga.
argentpepper

2

¡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]

1
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.


1
¿Qué sucede si el nombre de la columna comienza con "_"?
Anurag Uniyal

44
Me imagino que realmente no deberías nombrar tus columnas con un guión bajo. Si lo haces, no funcionará. Si es solo el extraño, que conoces, puedes modificarlo para agregar esas columnas.
Singletoned

1

Tengo una variación sobre la respuesta de Marco Mariani, expresada como decoradora. La principal diferencia es que manejará listas de entidades, además de ignorar de forma segura algunos otros tipos de valores de retorno (lo cual es muy útil al escribir pruebas usando 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

1

Para completar la respuesta de @Anurag Uniyal, aquí hay un método que seguirá recursivamente las relaciones:

from sqlalchemy.inspection import inspect

def to_dict(obj, with_relationships=True):
    d = {}
    for column in obj.__table__.columns:
        if with_relationships and len(column.foreign_keys) > 0:
             # Skip foreign keys
            continue
        d[column.name] = getattr(obj, column.name)

    if with_relationships:
        for relationship in inspect(type(obj)).relationships:
            val = getattr(obj, relationship.key)
            d[relationship.key] = to_dict(val) if val else None
    return d

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    first_name = Column(TEXT)
    address_id = Column(Integer, ForeignKey('addresses.id')
    address = relationship('Address')

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    city = Column(TEXT)


user = User(first_name='Nathan', address=Address(city='Lyon'))
# Add and commit user to session to create ids

to_dict(user)
# {'id': 1, 'first_name': 'Nathan', 'address': {'city': 'Lyon'}}
to_dict(user, with_relationship=False)
# {'id': 1, 'first_name': 'Nathan', 'address_id': 1}

en caso de que el valor predeterminado para 'with_relationships' se cambie a falso, mejor pase este valor a la llamada recursiva. es decir: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton

¿Cómo puedo lograr el resultado si quiero unirme a la tabla de usuario y dirección en base a la columna address_id y obtener toda la columna de la tabla de usuario y solo la columna de identificación de la tabla de direcciones?
Sudhakar

1

Con python 3.8+, podemos hacer esto con dataclass y el asdictmétodo que viene con él:

from dataclasses import dataclass, asdict

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer, create_engine

Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=False)


@dataclass
class User(Base):
    __tablename__ = 'users'

    id: int = Column(Integer, primary_key=True)
    name: str = Column(String)
    email = Column(String)

    def __init__(self, name):
        self.name = name
        self.email = 'hello@example.com'


Base.metadata.create_all(engine)

SessionMaker = sessionmaker(bind=engine)
session = SessionMaker()

user1 = User("anurag")
session.add(user1)
session.commit()

query_result = session.query(User).one()  # type: User
print(f'{query_result.id=:}, {query_result.name=:}, {query_result.email=:}')
# query_result.id=1, query_result.name=anurag, query_result.email=hello@example.com

query_result_dict = asdict(query_result)
print(query_result_dict)
# {'id': 1, 'name': 'anurag'}

La clave es usar el @dataclassdecorador y anotar cada columna con su tipo (la : strparte de la name: str = Column(String)línea).

También tenga en cuenta que, dado que emailno está anotado, no está incluido en query_result_dict.


En Python3.7 obtengo "NameError: el nombre 'asdict' no está definido"
devnull

¡Culpa mía! Es una función agregada en python 3.8. Se arregló mi respuesta.
toaruScar

Tan pitónico. 3.8 es genial. Pero realmente no necesitas el método init , ¿verdad? declarative y dataclass proporcionan métodos init genéricos.
Jeff Laughlin

0

Soy un programador de Python recién creado y tuve problemas para llegar a JSON con tablas unidas. Utilizando 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 para evitar tener alias o hacer que los campos colisionen.

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)

0

si la columna de la tabla de modelos no es equie mysql column.

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 lo usa de esta manera, 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}

Porque el nombre de los atributos de clase no es igual al almacén de columnas en mysql


0

Devuelve el contenido de esto: clase: .KeyedTuplecomo 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'}

0

Por el bien de todos y de mí, así es como 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]

0
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 del atributo es diferente a los nombres de columna.


0

Lo necesitará en todas partes en su proyecto, apriciado @anurag respondió que funciona bien. hasta este punto lo estaba usando, pero desordenará todo su código y tampoco funcionará con el cambio de entidad.

Más bien intente esto, herede su clase de consulta base en SQLAlchemy

from flask_sqlalchemy import SQLAlchemy, BaseQuery


class Query(BaseQuery):
    def as_dict(self):
        context = self._compile_context()
        context.statement.use_labels = False
        columns = [column.name for column in context.statement.columns]

        return list(map(lambda row: dict(zip(columns, row)), self.all()))


db = SQLAlchemy(query_class=Query)

después de eso, siempre que definas tu objeto, el método "as_dict" estará allí.


-1

En la mayoría de los escenarios, el nombre de la columna es adecuado para ellos. Pero tal vez 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 podía funcionar tan bien como antes.

sqlalchemy_base_model.py

También escribo la respuesta aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.