Convertir un objet sqlalchemy row en Python dict
y a-t-il une façon simple d'itérer des paires de noms et de valeurs de colonnes?
ma version de sqlalchemy est 0.5.6
Voici l'exemple de code où j'ai essayé d'utiliser dict( row), mais il lance exception, TypeError: l'objet 'User' n'est pas itérable
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)
exécuter ce code sur les sorties de mon système:
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
27 réponses
mise à jour 2018-03-25:
Vous ne pouvez plus utiliser u.__dict__
, comme il l'interprète comme une référence à une colonne/champ. Utilisez plutôt dict(u)
.
Réponse Originale:
vous pouvez accéder au __dict__
interne D'un objet SQLAlchemy, comme suit::
for u in session.query(User).all():
print u.__dict__
Je n'ai pas pu obtenir une bonne réponse donc j'utilise ceci:
def row2dict(row):
d = {}
for column in row.__table__.columns:
d[column.name] = str(getattr(row, column.name))
return d
Edit: si la fonction ci-dessus est trop longue et ne convient pas à certains goûts voici un one liner (python 2.7+)
row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
C'est la réponse correcte pour SQLAlchemy moderne (v0.8-v1.2+).
utiliser le système d'inspection .
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)
noter que .key
est le nom de l'attribut, qui peut être différent du nom de la colonne, par exemple dans le cas suivant:
class_ = Column('class', Text)
cette méthode fonctionne également pour column_property
.
ont une fonction _asdict()
qui donne 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'}
comme @balki l'a mentionné:
la méthode _asdict()
peut être utilisée si vous interrogez un champ spécifique parce qu'il est retourné comme un KeyedTuple .
In [1]: foo = db.session.query(Topic.name).first()
In [2]: foo._asdict()
Out[2]: {'name': u'blah'}
Tandis que, si vous ne spécifiez pas une colonne, vous pouvez utiliser l'une des autres méthodes proposées, comme celui fourni par @charlax. Notez que cette méthode n'est valable que pour 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'}
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)
question ancienne, mais depuis cela le premier résultat pour "sqlalchemy row to dict" dans Google, il mérite une meilleure réponse.
l'objet RowProxy que SqlAlchemy retourne a la méthode items (): http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items
il retourne simplement une liste de tuples (clé, valeur). Ainsi, on peut convertir une ligne en dict en utilisant ce qui suit:
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]
suite à la réponse de @balki, depuis SQLAlchemy 0.8 vous pouvez utiliser _asdict(), disponible pour les objets KeyedTuple. Cela rend une réponse assez simple à la question initiale. Il suffit de changer dans votre exemple les deux dernières lignes (la boucle for) pour celle-ci:
for u in session.query(User).all():
print u._asdict()
cela fonctionne parce que dans le code ci-dessus u est un objet de la classe de type KeyedTuple , depuis .all () retourne une liste de KeyedTuple. Par conséquent, il a la méthode _asdict () , qui retourne bien u comme un dictionnaire.
WRT la réponse par @STB: autant que je sache, anithong .all () returns est une liste de KeypedTuple. Par conséquent, les travaux ci-dessus soit si vous spécifiez une colonne ou non, aussi longtemps que vous traitez avec le résultat de .tous les() appliquée à un objet de Requête.
l'expression que vous itérez à travers évalue à la liste du modèle objets , pas des lignes. Ainsi, ce qui suit est l'usage correct d'eux:
for u in session.query(User).all():
print u.id, u.name
avez-vous vraiment besoin de les convertir en dicts? Bien sûr, il y a beaucoup de façons, mais alors vous n'avez pas besoin ORM partie de SQLAlchemy:
result = session.execute(User.__table__.select())
for row in result:
print dict(row)
Update : jetez un oeil au module sqlalchemy.orm.attributes
. Il a un ensemble de fonctions pour travailler avec des objets l'état, qui pourrait être utile pour vous, surtout instance_dict()
.
j'ai trouvé ce post parce que je cherchais un moyen de convertir une ligne SQLAlchemy en dict. Je suis en utilisant SqlSoup... mais la réponse a été construite par moi-même, donc, si cela pouvait aider quelqu'un voici mes deux cents:
a = db.execute('select * from acquisizioni_motes')
b = a.fetchall()
c = b[0]
# and now, finally...
dict(zip(c.keys(), c.values()))
en supposant que les fonctions suivantes seront ajoutées au class User
, la valeur suivante retournera toutes les paires de valeurs clés de toutes les colonnes:
def columns_to_dict(self):
dict_ = {}
for key in self.__mapper__.c.keys():
dict_[key] = getattr(self, key)
return dict_
contrairement aux autres réponses, tous les attributs de l'objet sont retournés sauf ceux qui sont Column
au niveau de la classe de l'objet. Par conséquent, aucun _sa_instance_state
ou tout autre attribut SQLalchemy ou vous ajoutez à l'objet sont inclus. référence
EDIT: Oublier de dire, que cela fonctionne aussi sur les hérité de Colonnes.
hybrid_propery
l'extension
si vous voulez aussi inclure hybrid_property
les attributs suivants fonctionneront:
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_
je suppose ici que vous marquez les colonnes avec un début _
pour indiquer que vous voulez les cacher, soit parce que vous accédez à l'attribut par un hybrid_property
ou vous ne voulez tout simplement pas les montrer. référence
Tipp all_orm_descriptors
retourne aussi hybrid_method et AssociationProxy si vous voulez aussi les inclure.
remarques à d'autres réponses
chaque réponse (comme 1 , 2 ) sur la base des __dict__
attribut renvoie simplement tous les attributs de l'objet. Ce pourrait être beaucoup plus d'attributs que vous voulez. Comme I sad Cela inclut _sa_instance_state
ou tout autre attribut que vous définissez sur cet objet.
chaque réponse (comme 1 , 2 ) qui est basé sur la fonction dict()
ne fonctionne que sur SQLalchemy row objets retournés par session.execute()
pas sur les classes que vous définissez pour travailler avec, comme le class User
de la question.
Le la résolution de réponse qui est basé sur row.__table__.columns
sûr pas de travail. row.__table__.columns
contient les noms des colonnes de la base de données SQL. Ces ne peuvent que être égaux au nom d'attribut de l'objet python. Si vous obtenez un AttributeError
.
Pour des réponses (comme 1 , 2 ) d'après class_mapper(obj.__class__).mapped_table.c
c'est la même chose.
Consultez Alex Brasetvik la Réponse de , vous pouvez utiliser une ligne de code pour résoudre le problème de
row_as_dict = [dict(row) for row in resultproxy]
dans la section commentaire de la réponse D'Alex Brasetvik, zzzeek le créateur de SQLAlchemy a déclaré que c'est la" méthode correcte " pour le problème.
vous pouvez convertir objet sqlalchemy à Dictionnaire comme ceci et le retourner comme JSON/dictionary.
fonctions D'aide:
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)
Pilote De La Fonction:
def all_products():
all_products = Products.query.all()
return to_array(all_products)
Voici comment Elixir le fait. La valeur de cette solution est qu'elle permet d'inclure récursivement la représentation des relations dans le dictionnaire.
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
Avec ce code, vous pouvez également ajouter à votre requête "filtre" ou "joindre" et ce travail!
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]
j'ai une variation sur la réponse de Marco Mariani, exprimée en décorateur. La principale différence est qu'il va gérer listes d'entités, ainsi que l'ignorance en toute sécurité certains autres types de valeurs de retour (qui est très utile lors de l'écriture de tests en utilisant des moqueries):
@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
deux voies:
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())
les docs offrent une solution très 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)
class User(object):
def to_dict(self):
return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])
ça devrait marcher.
je suis un nouveau programmeur Python et j'ai rencontré des problèmes pour atteindre JSON avec des tables jointes. En utilisant l'information des réponses ici j'ai construit une fonction pour retourner des résultats raisonnables à JSON où les noms de table sont inclus en évitant d'avoir à alias, ou avoir des champs de collision.
passez simplement le résultat d'une requête de session:
test = Session ().interrogation (Vminfo, client).join(client).order_by(VMInfo.vm_name).limite(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)
si votre colonne de la table models n'est pas égale à la colonne mysql.
tels que:
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())
à utiliser:
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 vous utilisez cette façon, vous pouvez obtenir modify_time et create_time les deux ne sont pas
{'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}
parce que le nom des attributs de classe n'est pas égal à la colonne stocker dans mysql
dans la plupart des scénarios, le nom de la colonne est approprié. Mais peut-être que vous écrivez le code comme suit:
class UserModel(BaseModel):
user_id = Column("user_id", INT, primary_key=True)
email = Column("user_email", STRING)
la colonne.nom " user_email "alors que le nom du champ est" email", la colonne.nom ne pouvait pas bien fonctionner comme avant.
une solution qui fonctionne aussi avec les classes héritées:
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])}
Voici une façon très simple de le faire
row2dict = lambda r: dict(r.items())
Je n'ai pas beaucoup d'expérience avec cela, mais ce qui suit semble fonctionner pour ce que je fais:
dict(row)
cela semble trop simple (comparé aux autres réponses ici). Ce qui me manque?
Mon prendre en utilisant (trop?) dictionnaires:
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
tournant avec flask (y compris jsonify) et flask_sqlalchemy pour imprimer des sorties comme JSON.
appelle la fonction avec jsonify(serialize()).
fonctionne avec toutes les requêtes SQLAlchemy que j'ai essayé jusqu'à présent (exécution SQLite3)