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
159
demandé sur Anurag Uniyal 2009-12-24 15:42:34

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__
159
répondu hllau 2018-03-26 01:38:38

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}
113
répondu Anurag Uniyal 2014-05-02 12:54:03

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 .

54
répondu RazerM 2017-10-30 17:03:46
for row in resultproxy:
    row_as_dict = dict(row)
45
répondu Alex Brasetvik 2009-12-24 12:45:04
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'}
24
répondu balki 2014-01-09 19:53:55

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'}
14
répondu Sam Bourne 2014-09-06 00:56:14
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)
12
répondu Marco Mariani 2010-03-16 13:24:01

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]
12
répondu fgasparini 2016-10-27 21:00:51

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.

9
répondu jgbarah 2014-08-22 11:37:49

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

7
répondu Denis Otkidach 2009-12-25 06:11:36

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()))
7
répondu Mychot sad 2012-08-04 16:31:49

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.

6
répondu F.Raab 2017-05-23 12:26:24

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.

5
répondu NorWay 2017-07-04 07:12:01

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)
3
répondu Chirag Vora 2018-05-03 07:49:05

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
2
répondu argentpepper 2016-03-10 21:21:30

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]
2
répondu Yakir Tsuberi 2017-11-05 09:39:17

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
1
répondu Chris R 2010-07-28 17:32:21

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())
1
répondu FrostSigh 2018-02-06 23:57:47

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)
1
répondu J.B. 2018-09-06 15:55:06
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.

0
répondu Singletoned 2010-02-10 14:07:02

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)
0
répondu tknightowl 2015-11-24 15:45:49

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

0
répondu 张小诚 2018-06-27 09:34:00

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.

sqlalchemy_base_model.py

j'écris aussi la réponse ici

-1
répondu kaka_ace 2017-05-23 12:03:08

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])}
-1
répondu magne4000 2016-06-08 14:11:54

Voici une façon très simple de le faire

row2dict = lambda r: dict(r.items())
-2
répondu user2444711 2013-06-02 07:54:19

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?

-2
répondu Ralph Case 2016-05-18 18:21:58

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)

-3
répondu Sawyer Brooks 2016-06-08 15:13:18