web-dev-qa-db-fra.com

Convertir un objet de la ligne sqlalchemy en python dict

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
207
Anurag Uniyal

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}
131
Anurag Uniyal

Vous pouvez accéder au __dict__ interne d'un objet SQLAlchemy, comme suit:

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

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 .

68
RazerM
for row in resultproxy:
    row_as_dict = dict(row)
67
Alex Brasetvik

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'}
32
balki

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'}
17
Sam Bourne

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]
15
fgasparini
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
Marco Mariani

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 extension

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

Remarques sur d'autres réponses

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.

10
F.Raab

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.

10
jgbarah

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

8
Denis Otkidach

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.

7
NorWay

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

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

5
Enmanuel Medina

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)
4
Chirag Vora

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

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
2
argentpepper

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)
2
yellow-saint

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]
2
Yakir Tsuberi

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
1
Chris R

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

1
Singletoned

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'}
0
Eds_k

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

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

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.

0
Yash Pokar

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]
0
bigdatamann

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)
0
tknightowl