Converti l'oggetto riga sqlalchemy in python dict


241

Esiste un modo semplice per scorrere le coppie nome-valore e colonna?

La mia versione di sqlalchemy è 0.5.6

Ecco il codice di esempio in cui ho provato a utilizzare dict (riga), ma genera un'eccezione, TypeError: l'oggetto 'Utente' non è iterabile

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)

Esecuzione di questo codice sulle uscite del mio 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
Il titolo della domanda non corrisponde alla domanda stessa. Secondo i documenti Le righe dei risultati restituite da Query che contengono più entità ORM e / o espressioni di colonna fanno uso di questa classe per restituire righe. dove questa classe è sqlalchemy.util.KeyedTuplequale è l' oggetto riga dal titolo della domanda. Tuttavia, la query nella domanda utilizza la classe del modello (mappata), quindi il tipo di oggetto riga è la classe del modello anziché sqlalchemy.util.KeyedTuple.
Piotr Dobrogost,

2
La domanda di @PiotrDobrogost è del 2009 e menziona sqlalchemy versione 0.5.6
Anurag Uniyal

Risposte:


233

È possibile accedere all'interno __dict__di un oggetto SQLAlchemy, come il seguente:

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

24
La migliore risposta in questo thread, non so perché tutti gli altri stanno proponendo soluzioni molto più complicate.
Dave Rawks,

92
Ciò fornisce un ulteriore campo "_sa_instance_state", almeno nella versione 0.7.9.
elbear,

21
quindi sarebbe meglio:dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
lyfing

6
questo non sembra l'ideale poiché, come sottolineato dalla gente, è __dict__inclusa una _sa_instance_statevoce che deve essere rimossa. se esegui l'upgrade a una versione futura e vengono aggiunti altri attributi, potresti dover tornare indietro e gestirli manualmente. se vuoi solo i dati di colonna (ad esempio, per prendere un elenco di istanze da una query e rilasciarle in un frame di dati Panda), {col.name: getattr(self, col.name) for col in self.__table__.columns}come risposto da Anurag Uniyal (con importanti correzioni dai commenti a quella risposta) sembra sia più conciso che errore- prova.
Kilgoretrout,

14
Questa risposta è sbagliata La domanda ha anche dict(u)e afferma correttamente che genera a TypeError.
RazerM,

146

Non ho potuto ottenere una buona risposta, quindi uso questo:

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

    return d

Modifica: se la funzione sopra è troppo lunga e non adatta ad alcuni gusti, ecco una riga (python 2.7+)

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

17
Più sinteticamente, return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()).
Argentpepper,

4
@argentpepper sì, potresti persino farlo row2dict = lambda row: dict((col, getattr(row, col)) for col in row.__table__.columns.keys())per renderlo una vera fodera, ma preferisco che il mio codice sia leggibile, orizzontalmente corto, verticalmente lungo
Anurag Uniyal

14
Cosa succede se la mia colonna non è assegnata a un attributo con lo stesso nome? IE, x = Column('y', Integer, primary_key=True)? Nessuna di queste soluzioni funziona in questo caso.
Buttons840,

13
drdaeman ha ragione, ecco il frammento corretto:return {c.name: getattr(self, c.name) for c in self.__table__.columns}
charlax,

5
Questa risposta fa un'ipotesi non valida: i nomi delle colonne non corrispondono necessariamente ai nomi degli attributi.
RazerM,

95

Come da @zzzeek nei commenti:

si noti che questa è la risposta corretta per le versioni moderne di SQLAlchemy, supponendo che "riga" sia un oggetto riga principale, non un'istanza mappata ORM.

for row in resultproxy:
    row_as_dict = dict(row)

13
Dice 'L'oggetto XXX non è iterabile', sto usando 0.5.6, ottengo da session.query (Klass) .filter (). All ()
Anurag Uniyal

60
si noti che questa è la risposta corretta per le versioni moderne di SQLAlchemy, supponendo che "riga" sia un oggetto riga principale, non un'istanza mappata ORM.
zzzeek,

48
Inoltre, zzzeek è il creatore di sqlalchemy.
chris,

1
Qualcuno può approfondire questo? Sono un noob e non capisco.
lameei,

1
Qual è la differenza tra un oggetto riga core rispetto a un'istanza mappata ORM? Questo non funziona per me sulle righe di query(MyModel).all(): oggetto MyModel non è iterabile.
Jonathan Hartley,

81

In SQLAlchemy v0.8 e versioni successive, utilizzare il sistema di ispezione .

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)

Si noti che .keyè il nome dell'attributo, che può essere diverso dal nome della colonna, ad esempio nel caso seguente:

class_ = Column('class', Text)

Questo metodo funziona anche per column_property.


@DukeDougal Penso che funzioni dalla v0.8 (quando è stato aggiunto il sistema di ispezione).
RazerM,

1
Funziona con Sqlalchemy v2.0. Altre risposte no.
Thanh Nguyen,

Questo non tiene conto delle colonne differite
Segna il

1
@Mark Non è chiaro per me che dovrebbero essere esclusi per impostazione predefinita. Tuttavia, puoi verificare che le chiavi non siano presentisqlalchemy.inspect(obj).unloaded
RazerM

5
@NguyenThanh Lavorare con SQLAlchemy v2.0 è particolarmente impressionante data la sua inesistenza! L'ultima versione (beta) è v1.3.0b1.
Mark Amery,

39

le righe hanno una _asdict()funzione che dà 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'}

Dovrebbe essere privato e non potrebbe essere rimosso / modificato nelle versioni future.
balki,

2
@balki È abbastanza ben documentato e come tale non del tutto privato. Sebbene un carattere di sottolineatura principale abbia quel significato in Python in generale, qui è probabilmente usato per non scontrarsi con possibili chiavi di tupla.
Ilja Everilä,

5
Funziona solo con KeyedTuple, che vengono restituiti solo quando si eseguono query su campi specifici di una riga. vale a dire .query (Topic.name) restituisce un KeyedTuple, mentre .query (Topic) restituisce un Topic, che non ha ._asdict () - Derp. ho appena visto la risposta degli STB di seguito.
Chad Lowe,

20

come menzionato @balki:

Il _asdict()metodo può essere utilizzato se si esegue una query su un campo specifico perché viene restituito come KeyedTuple .

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

Considerando che, se non si specifica una colonna, è possibile utilizzare uno degli altri metodi proposti, come quello fornito da @charlax. Si noti che questo metodo è valido solo per 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'}

Se gli attributi della classe ORM di Python hanno nomi diversi dalle colonne del database, prova questa soluzione: stackoverflow.com/questions/27947294/…
TaiwanGrapefruitTea

2
in realtà, una soluzione migliore per tutti i casi è fornita dall'autore sqlalchemy a stackoverflow.com/a/27948279/1023033
TaiwanGrapefruitTea

Quando provo questo ottengo l'oggetto ResultProxy non ha alcun attributo '_asdict'
slashdottir

@slashdottir, stai eseguendo il tuo oggetto query (chiamando il .first()metodo)?
Sam Bourne,

1
Questa risposta fa un'ipotesi non valida: i nomi delle colonne non corrispondono necessariamente ai nomi degli attributi - vedi la risposta di RazerM.
Piotr Dobrogost,

18

Vecchia domanda, ma dato che questo è il primo risultato di "riga sqlalchemy da dettare" in Google, merita una risposta migliore.

L'oggetto RowProxy restituito da SqlAlchemy ha il metodo items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

Restituisce semplicemente un elenco di tuple (chiave, valore). Quindi si può convertire una riga in dict usando quanto segue:

In Python <= 2.6:

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

In Python> = 2.7:

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

13
Puoi semplicemente farelist_of_dicts = [dict(row.items()) for row in rows]
Markus Meskanen il

Un problema è che i nomi delle colonne che SQLAlchemy utilizza in un set di risultati sono table_name_column_name, se si desidera nomi diversi (ad es. Solo column_name), utilizzare il .labelmetodo session.query( MyTable.column_name.label('column_name'), ... )
Aneel,

Ciao, sto ricevendo questo problema, per favore aiutami * datetime.datetime (2018, 11, 24, 18, 52, 50) non è serializzabile JSON *
Saravanan Nandhan,

13

Supponendo che le seguenti funzioni verranno aggiunte alle class Userseguenti restituirà tutte le coppie chiave-valore di tutte le colonne:

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

a differenza delle altre risposte vengono restituite tutte, ma solo quegli attributi dell'oggetto che sono Columnattributi a livello di classe dell'oggetto. Pertanto, nessun _sa_instance_stateo nessun altro attributo SQLalchemy o aggiunto all'oggetto sono inclusi. Riferimento

EDIT: dimentica di dire che questo funziona anche su colonne ereditate.

hybrid_propery extention

Se si desidera includere anche gli hybrid_propertyattributi, funzionerà quanto segue:

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_

Presumo qui che si contrassegni le colonne con un inizio _per indicare che si desidera nasconderle, o perché si accede all'attributo da un hybrid_propertyo semplicemente non si desidera mostrarle. Riferimento

Tipp all_orm_descriptors restituisce anche hybrid_method e AssociationProxy se si desidera includerli anche.

Commenti ad altre risposte

Ogni risposta (come 1 , 2 ) che in base __dict__all'attributo restituisce semplicemente tutti gli attributi dell'oggetto. Questo potrebbe essere molto più attributi di quanto tu voglia. Come purtroppo questo include _sa_instance_stateo qualsiasi altro attributo definito su questo oggetto.

Ogni risposta (come 1 , 2 ) che si basa sulla dict()funzione funziona solo su oggetti riga di SQLalchemy restituiti session.execute()non dalle classi con cui si definisce il lavoro, come il class Userdalla domanda.

La risposta risolutiva su cui si baserà row.__table__.columnssicuramente non funzionerà. row.__table__.columnscontiene i nomi delle colonne del database SQL. Questi possono essere solo uguali al nome degli attributi dell'oggetto python. Altrimenti ottieni un AttributeError. Per le risposte (come 1 , 2 ) basate su di class_mapper(obj.__class__).mapped_table.cesso è lo stesso.


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)

4
Essere consapevoli della differenza tra local_table e mapped_table. Ad esempio, se applichi una sorta di eredità di tabella nel tuo db (tbl_employees> tbl_managers, tbl_employees> tbl_staff), le tue classi mappate dovranno riflettere questo (Manager (Employee), Staff (Employee)). mapped_table.c ti darà i nomi delle colonne sia della tabella base che della tabella ereditaria. local_table ti dà solo il nome della tua tabella (ereditaria).
Michael Ekoka,

Questo evita di dare il campo '_sa_instance_state', almeno nella versione 0.8+.
Evan Siroky,

4
Questa risposta fa un'ipotesi non valida: i nomi delle colonne non corrispondono necessariamente ai nomi degli attributi.
RazerM,

11

Dopo la risposta di @balki, da SQLAlchemy 0.8 è possibile utilizzare _asdict (), disponibile per gli oggetti KeyedTuple. Questo rende una risposta piuttosto semplice alla domanda originale. Basta cambiare nel tuo esempio le ultime due righe (il ciclo for) per questo:

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

Questo funziona perché nel codice precedente u è un oggetto di tipo classe KeyedTuple , poiché .all () restituisce un elenco di KeyedTuple. Pertanto ha il metodo _asdict () , che ti restituisce come un dizionario.

SCRIVI la risposta di @STB: AFAIK, tra cui restituisce .all () è un elenco di KeypedTuple. Pertanto, quanto sopra funziona se si specifica una colonna o meno, purché si tratti del risultato di .all () applicato a un oggetto Query.


6
Questo potrebbe essere stato vero in passato, ma su SQLAlchemy v1.0 .all()restituisce un elenco di Useristanze, quindi non funziona.
RazerM,

@RazerM, scusa, ma non capisco cosa intendi. Il ciclo for dovrebbe scorrere esattamente l'elenco delle istanze dell'utente, convertendole (u) in dizionari e quindi stampandole ...
jgbarah

3
Userle istanze non hanno un _asdictmetodo. Vedi gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
RazerM,

2
Ora ho capito. Grazie. Invece di KeyedTuple, ora .all () restituisce oggetti utente. Quindi il problema per la versione 1.0 (e successive, suppongo) è come ottenere un dizionario da un oggetto Utente. Grazie per il chiarimento.
Jgbarah,

10

L'espressione che stai ripetendo restituisce un elenco di oggetti modello , non di righe. Quindi il seguente è il loro corretto utilizzo:

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

Hai davvero bisogno di convertirli in cubetti? Certo, ci sono molti modi, ma poi non hai bisogno della parte ORM di SQLAlchemy:

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

Aggiornamento : dai un'occhiata al sqlalchemy.orm.attributesmodulo. Ha una serie di funzioni per lavorare con lo stato dell'oggetto, che potrebbero esserti utili, in particolare instance_dict().


2
Voglio convertirli in dict, perché qualche altro codice ha bisogno di dati come dict, e voglio un modo generico perché non saprò quali colonne ha un oggetto modello
Anurag Uniyal,

e quando riesco a gestirli, ho accesso solo agli oggetti modello, quindi non posso usare session.execute ecc.
Anurag Uniyal,

8

Fare riferimento alla risposta di Alex Brasetvik , è possibile utilizzare una riga di codice per risolvere il problema

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

Nella sezione commenti della risposta di Alex Brasetvik, zzzeek il creatore di SQLAlchemy ha dichiarato che questo è il "metodo corretto" per il problema.


1
@Greenonline Certo, il commento di approvazione è sotto la risposta di Alex Brasetvik. Modificato per aggiungere un link alla sua risposta
NorWay

Qual è il resultproxy?
lameei,

8

Potresti provare a farlo in questo modo.

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

Utilizza un metodo incorporato nell'oggetto query che restituisce un oggetto dictonary dell'oggetto query.

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


1
Aggiungi qualche spiegazione in più forse?
Tiw

1
Niente di più da spiegare. È un metodo incorporato sull'oggetto risultato. Quindi, sia che lo facciate per tutti i risultati, sia per una singola riga, esiste un _asdict()metodo integrato che essenzialmente comprime i nomi dei campi con i valori dei campi e restituisce il risultato come dizionario.
Matteo,

Molto conciso e vorrei che funzionasse, ma unel mio caso è una stringa e ricevo l'errore `` L'oggetto modello 'non ha attributo' _asdict'` @hllau di seguito ha funzionato per me
Mote Zart,

7

Ho trovato questo post perché stavo cercando un modo per convertire una riga SQLAlchemy in un dict. Sto usando SqlSoup ... ma la risposta è stata costruita da me, quindi, se potesse aiutare qualcuno, ecco i miei due centesimi:

a = db.execute('select * from acquisizioni_motes')
b = a.fetchall()
c = b[0]

# and now, finally...
dict(zip(c.keys(), c.values()))

1
oppure, se si preferisce ..: [dict (zip (i.keys (), i.values ​​())) per i in b]
Mychot triste

Questa è l'unica sintassi che ho trovato che funziona davvero! Ho provato cose per oltre un'ora.
slashdottir,

Per le selezioni di base, il RowProxy( cin questa risposta) aderisce al protocollo di mappatura, quindi puoi semplicemente chiamare dict(c).
RazerM,

4

È possibile convertire l'oggetto sqlalchemy in dizionario come questo e restituirlo come json / dizionario.

Funzioni di supporto:

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) 

Funzione conducente:

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

3

Due strade:

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


2

Ecco come fa Elisir. Il valore di questa soluzione è che consente di includere ricorsivamente la rappresentazione del dizionario delle relazioni.

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

Il link è morto. La prossima volta copia qui anche il codice pertinente.
Gus E

Lo farà la prossima volta. Se ricordo bene, la funzione era piuttosto lunga.
Argentpepper,

2

Con questo codice puoi anche aggiungere alla tua query "filtro" o "join" e questo lavoro!

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("_")])

Dovrebbe funzionare.


1
cosa succede se il nome della colonna inizia con "_"?
Anurag Uniyal,

4
Immagino che non dovresti davvero dare un nome alle tue colonne con un trattino basso. Se lo fai, non funzionerà. Se è solo quello strano, che conosci, potresti modificarlo per aggiungere quelle colonne.
Singletoned

1

Ho una variazione sulla risposta di Marco Mariani, espressa come decoratrice. La differenza principale è che gestirà elenchi di entità, oltre a ignorare in modo sicuro alcuni altri tipi di valori restituiti (che è molto utile quando si scrivono test usando i mock):

@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

Per completare la risposta di @Anurag Uniyal, ecco un metodo che seguirà ricorsivamente le relazioni:

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}

nel caso in cui il valore predefinito per "with_relationships" sia cambiato in false, è meglio passare questo valore alla chiamata ricorsiva. cioè: d[relationship.key] = to_dict(val,with_relationships) if val else None
Nicholas Hamilton il

come posso ottenere il risultato, se voglio unire l'utente e la tabella degli indirizzi in base alla colonna address_id e recuperare tutta la colonna dalla tabella dell'utente e solo la colonna id dalla tabella degli indirizzi.
Sudhakar,

1

Con python 3.8+, possiamo farlo con la dataclass e il asdictmetodo che ne deriva:

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 chiave è usare il @dataclassdecoratore e annotare ogni colonna con il suo tipo (la : strparte della name: str = Column(String)linea).

Si noti inoltre che poiché emailnon è annotato, non è incluso in query_result_dict.


Su Python3.7 ottengo "NameError: il nome 'asdict' non è definito"
devnull

Colpa mia! È una funzione aggiunta in Python 3.8. Risolto il problema con la mia risposta.
toaruScar

Così pitonico. 3.8 è fantastico. Ma non hai davvero bisogno del metodo init , vero? dichiarative e dataclass forniscono entrambi metodi di inizializzazione generici.
Jeff Laughlin,

0

Sono un programmatore Python coniato di recente e ho riscontrato problemi nell'arrivare a JSON con le tabelle unite. Usando le informazioni dalle risposte qui ho creato una funzione per restituire risultati ragionevoli a JSON in cui sono inclusi i nomi delle tabelle evitando di dover alias o far collidere i campi.

Basta passare il risultato di una query di sessione:

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

json = sqlAl2json (test)

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

se la colonna della tabella dei modelli non è la colonna mysql equie.

ad esempio :

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

Bisogno di usare:

 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}

se lo usi in questo modo puoi ottenere edit_time e create_time entrambi Nessuno

{'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}

Perché il nome degli attributi di classe non è uguale all'archivio di colonne in mysql


0

Restituisce il contenuto di questo: class: .KeyedTuplecome dizionario

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

Per il bene di tutti e di me stesso, ecco come 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))

Questa funzione potrebbe essere d'aiuto. Non riesco a trovare una soluzione migliore per risolvere il problema quando il nome dell'attributo è diverso dai nomi delle colonne.


0

Ne avrai bisogno ovunque nel tuo progetto, ho apprezzato @anurag ha risposto che funziona benissimo. fino a questo punto lo stavo usando, ma rovinerà tutto il tuo codice e non funzionerà con il cambio di entità.

Prova piuttosto, eredita la tua classe di query di base in 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)

dopodiché ovunque definirai il tuo oggetto il metodo "as_dict" sarà lì.


-1

Nella maggior parte degli scenari, il nome della colonna è adatto a loro. Ma forse scrivi il codice come segue:

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

column.name "user_email" mentre il nome del campo è "email", il column.name non potrebbe funzionare bene come prima.

sqlalchemy_base_model.py

anche io scrivo la risposta qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.