Existe-t-il un moyen simple de parcourir les paires nom de colonne et valeur?
Ma version de sqlalchemy est 0.5.6
Voici l'exemple de code dans lequel j'ai essayé d'utiliser dict (row), mais une exception est générée. 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()):
__table= '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)
L'exécution de 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
Je ne pouvais pas obtenir une bonne réponse alors 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}
Vous pouvez accéder au __dict__
interne d'un objet SQLAlchemy, comme suit:
for u in session.query(User).all():
print u.__dict__
Dans SQLAlchemy v0.8 et plus récent, utilisez 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)
_
Notez 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
.
for row in resultproxy:
row_as_dict = dict(row)
les lignes 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 mentionné:
La méthode _asdict()
peut être utilisée si vous interrogez un champ spécifique car celui-ci est renvoyé sous la forme KeyedTuple .
In [1]: foo = db.session.query(Topic.name).first()
In [2]: foo._asdict()
Out[2]: {'name': u'blah'}
Par contre, si vous ne spécifiez pas de colonne, vous pouvez utiliser l’une des autres méthodes proposées - telle que celle fournie 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'}
Vieille question, mais depuis lors, le premier résultat de "sqlalchemy row to dict" dans Google mérite une meilleure réponse.
L'objet RowProxy renvoyé par SqlAlchemy 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). On peut donc convertir une ligne en dict en utilisant ce qui suit:
Dans Python <= 2.6:
rows = conn.execute(query)
list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows]
Dans 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)
En supposant que les fonctions suivantes soient ajoutées au class User
, les opérations suivantes renverront toutes les paires clé-valeur 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 sauf les attributs de l’objet sont renvoyés, qui sont Column
attributs au niveau classe de l’objet. Par conséquent, ni _sa_instance_state
ni aucun autre attribut , SQLalchemy ou que vous ajoutez à l'objet ne sont inclus. Référence
EDIT: Oubliez de dire que cela fonctionne également sur les colonnes héritées.
hybrid_propery
extensionSi vous souhaitez également inclure hybrid_property
attributs, les éléments 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 souhaitez les masquer, soit parce que vous accédez à l'attribut par un hybrid_property
, soit vous ne voulez tout simplement pas les afficher. Référence
Tipp all_orm_descriptors
renvoie également hybrid_method et AssociationProxy si vous souhaitez également les inclure.
Chaque réponse (comme 1 , 2 ) basée sur l'attribut __dict__
renvoie simplement tous les attributs de l'objet. Cela pourrait être beaucoup plus d'attributs que vous le souhaitez. Comme je suis triste, cela inclut _sa_instance_state
ou tout autre attribut que vous définissez sur cet objet.
Chaque réponse (comme 1 , 2 ) basée sur la fonction dict()
ne fonctionne que sur SQLalchemy rangée les objets renvoyés par session.execute()
et non sur les classes que vous définissez avec lesquelles travailler, comme le class User
de la question.
Le solution de réponse qui est basé sur row.__table__.columns
fonctionnera certainement et non . row.__table__.columns
contient les noms de colonne de la base de données SQL. Ceux-ci peuvent uniquement être égaux au nom d'attribut de l'objet python. Sinon, vous obtenez un AttributeError
. Pour les réponses (comme 1 , 2 ) basées sur class_mapper(obj.__class__).mapped_table.c
c'est la même chose.
Après @balki answer, 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 type classe KeyedTuple , car .all () renvoie une liste de KeyedTuple. Par conséquent, il a la méthode _ asdict () , qui renvoie joliment u comme dictionnaire.
WRT la réponse par @STB: AFAIK, et que .all () renvoie une liste de KeypedTuple. Par conséquent, ce qui précède fonctionne que vous spécifiiez une colonne ou non, tant que vous traitez avec le résultat de .all () appliqué à un objet Query.
L'expression que vous parcourez est évaluée en tant que liste d'objets de modèle et non de lignes. Donc, voici l'utilisation correcte d'eux:
for u in session.query(User).all():
print u.id, u.name
Avez-vous vraiment besoin de les convertir en dict? Bien sûr, il y a beaucoup de façons, mais vous n'avez pas besoin de la partie ORM de SQLAlchemy:
result = session.execute(User.__table__.select())
for row in result:
print dict(row)
Mise à jour: Regardez le module sqlalchemy.orm.attributes
. Il a un ensemble de fonctions pour travailler avec l'état d'objet, ce qui pourrait vous être utile, en particulier instance_dict()
.
Reportez-vous à Réponse d'Alex Brasetvik , vous pouvez utiliser une ligne de code pour résoudre le problème
row_as_dict = [dict(row) for row in resultproxy]
Dans la section commentaire de la réponse d'Alex Brasetvik, le créateur de SQLAlchemy, zzzeek, a déclaré qu'il s'agissait de la "méthode correcte" pour le problème.
J'ai trouvé ce post parce que je cherchais un moyen de convertir une ligne SQLAlchemy en dict. J'utilise SqlSoup ... mais la réponse a été élaborée par moi-même. Si cela peut aider quelqu'un, voici mes deux sous:
a = db.execute('select * from acquisizioni_motes')
b = a.fetchall()
c = b[0]
# and now, finally...
dict(Zip(c.keys(), c.values()))
Vous pouvez essayer de le faire de cette façon.
for u in session.query(User).all():
print(u._asdict())
Il utilise une méthode intégrée dans l'objet de requête qui renvoie un objet dictonaire de l'objet de requête.
références: https://docs.sqlalchemy.org/en/latest/orm/query.html
Vous pouvez convertir un objet sqlalchemy en dictionnaire comme celui-ci et le renvoyer sous la forme json/dictionary.
Fonctions d'assistance:
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)
Fonction du conducteur:
def all_products():
all_products = Products.query.all()
return to_array(all_products)
Deux façons:
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())
Voici comment Elixir le fait. L'intérêt de cette solution est qu'elle permet d'inclure récursivement la représentation dans le dictionnaire des relations.
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
La documentation offre 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)
Avec ce code, vous pouvez également ajouter à votre requête "filtre" ou "rejoindre" 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 de la réponse de Marco Mariani, exprimée en tant que décoratrice. La principale différence est qu’il gérera listes d’entités, tout en ignorant en toute sécurité d’autres types de valeurs de retour (ce qui est très utile lors de l’écriture de tests à l’aide de simulacres):
@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
Pour compléter la réponse de @Anurag Uniyal, voici une méthode qui suivra de manière récursive les relations:
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):
__table= 'users'
id = Column(Integer, primary_key=True)
first_name = Column(TEXT)
address_id = Column(Integer, ForeignKey('addresses.id')
address = relationship('Address')
class Address(Base):
__table= '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}
class User(object):
def to_dict(self):
return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])
Cela devrait fonctionner.
Retourne le contenu de ceci: class: .KeyedTuple
sous forme de dictionnaire
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'}
si votre colonne de table de modèles n'est pas equie mysql column.
tel 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())
Besoin d'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 méthode, vous pouvez obtenir modify_time et create_time, qui ne sont aucun
{'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 au magasin de colonnes dans mysql
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))
Cette fonction pourrait aider. Je ne trouve pas de meilleure solution pour résoudre le problème lorsque le nom de l'attribut est différent de celui de la colonne.
Vous en aurez besoin partout dans votre projet, j'ai apriciate @anurag répondu que cela fonctionnait bien. Jusque-là, je l'utilisais, mais tout votre code serait dérangé et ne fonctionnerait pas avec le changement d'entité.
Essayez plutôt ceci, héritez de votre classe de requête de base dans 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)
ensuite, partout où vous définirez votre objet, la méthode "as_dict" sera présente.
Dans l’intérêt de tous et de moi-même, voici comment je l’utilise:
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]
Je suis un nouveau programmeur Python et j'ai eu des problèmes pour obtenir JSON avec les tables jointes. En utilisant les informations des réponses ici, j'ai créé une fonction pour renvoyer des résultats raisonnables à JSON, dans laquelle les noms de table sont inclus, ce qui évite d'avoir à créer un alias ou à faire entrer des champs en collision.
Passez simplement le résultat d'une requête de session:
test = Session (). query (VMInfo, Client) .join (Client) .order_by (VMInfo.nom_vm) .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)