471

Is there a simple way to iterate over column name and value pairs?

My version of SQLAlchemy is 0.5.6

Here is the sample code where I tried using dict(row):

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)

Running this code on my system outputs:

Traceback (most recent call last):
  File "untitled-1.py", line 37, in <module>
    print dict(u)
TypeError: 'User' object is not iterable
2
  • 3
    The title of the question does not match the question itself. According to docs Result rows returned by Query that contain multiple ORM entities and/or column expressions make use of this class to return rows. where this class is sqlalchemy.util.KeyedTuple which is row object from the question's title. However query in the question uses model (mapped) class thus the type of row object is the model class instead of sqlalchemy.util.KeyedTuple. Commented Feb 2, 2018 at 9:07
  • 8
    @PiotrDobrogost Question is from 2009 and mentions sqlalchemy version 0.5.6 Commented Mar 1, 2018 at 8:16

50 Answers 50

399

You may access the internal __dict__ of a SQLAlchemy object, like the following:

for u in session.query(User).all():
    print u.__dict__
23
  • 137
    This gives an extra '_sa_instance_state' field, at least in version 0.7.9.
    – elbear
    Commented Oct 29, 2012 at 13:04
  • 50
    so this would be better: dictret = dict(row.__dict__); dictret.pop('_sa_instance_state', None)
    – Lyfing
    Commented Nov 5, 2014 at 8:39
  • 29
    this seems not ideal since as people have pointed out __dict__ includes an _sa_instance_state entry which must then be removed. if you upgrade to a future version and other attributes are added you may have to go back and manually deal with them. if you want just column data (for example, to take a list of instances from a query and drop them in a pandas dataframe) then {col.name: getattr(self, col.name) for col in self.__table__.columns} as answered by Anurag Uniyal (with important corrections from comments to that answer) seems both more succinct and error-proof. Commented Jul 30, 2016 at 20:44
  • 34
    This answer is wrong. The question even has dict(u) and correctly states that it throws a TypeError.
    – RazerM
    Commented May 29, 2018 at 11:36
  • 31
    All of this is still insane. Why can't SQLAlchemy results just be serialized with jsonify?
    – ajbraus
    Commented Oct 12, 2019 at 23:05
264

As per @zzzeek in comments:

note that this is the correct answer for modern versions of SQLAlchemy, assuming "row" is a core row object, not an ORM-mapped instance.

for row in resultproxy:
    row_as_dict = row._mapping  # SQLAlchemy 1.4 and greater
    # row_as_dict = dict(row)  # SQLAlchemy 1.3 and earlier

background on row._mapping, new as of SQLAlchemy 1.4: https://docs.sqlalchemy.org/en/stable/core/connections.html#sqlalchemy.engine.Row._mapping

19
  • 25
    It says 'XXX object is not iterable', I am using 0.5.6, i get by session.query(Klass).filter().all() Commented Dec 24, 2009 at 13:13
  • 101
    note that this is the correct answer for modern versions of SQLAlchemy, assuming "row" is a core row object, not an ORM-mapped instance.
    – zzzeek
    Commented Nov 24, 2014 at 17:46
  • 107
    Also note that zzzeek is the creator of sqlalchemy.
    – chris
    Commented Aug 24, 2016 at 18:32
  • 12
    What is the difference between a core row object versus an ORM-mapped instance? This doesn't work for me on the rows from of query(MyModel).all(): MyModel object is not iterable. Commented Dec 13, 2019 at 22:20
  • 32
    This answer is unhelpful as you're not outlining how or what is "resultproxy"? Commented Feb 21, 2020 at 15:03
207

I couldn't get a good answer so I use this:

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

    return d

Edit: if above function is too long and not suited for some tastes here is a one liner (python 2.7+)

row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
12
  • 22
    More succinctly, return dict((col, getattr(row, col)) for col in row.__table__.columns.keys()). Commented Mar 30, 2012 at 19:13
  • 16
    What if my Column isn't assigned to an attribute of the same name? IE, x = Column('y', Integer, primary_key=True) ? None of these solutions work in this case.
    – Buttons840
    Commented May 31, 2012 at 20:46
  • 5
    Warning: __table__.columns.keys() won't work, because columns dictionary keys are not always strings (as getattr requires), but possibly all sorts of objects like sqlalchemy.sql.expression._truncated_label. Using c.name instead of c works for me.
    – drdaeman
    Commented Jul 14, 2012 at 17:49
  • 18
    drdaeman is right, here is the correct snippet: return {c.name: getattr(self, c.name) for c in self.__table__.columns}
    – charlax
    Commented Aug 9, 2012 at 13:41
  • 13
    This answer makes an invalid assumption: column names don't necessarily match attribute names.
    – RazerM
    Commented May 20, 2016 at 15:52
130

In SQLAlchemy v0.8 and newer, use the inspection system.

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)

Note that .key is the attribute name, which can be different from the column name, e.g. in the following case:

class_ = Column('class', Text)

This method also works for column_property.

5
  • @DukeDougal I think this works from v0.8 (when the inspection system was added).
    – RazerM
    Commented Sep 11, 2016 at 13:01
  • This doesn't take into account deferred columns
    – Mark
    Commented Dec 3, 2017 at 23:35
  • 1
    @Mark It's not clear to me that they should be excluded by default. Nevertheless, you can check that the keys aren't in sqlalchemy.inspect(obj).unloaded
    – RazerM
    Commented Dec 4, 2017 at 9:05
  • While I won't use this for the results of query, inspect was very useful when using insert_many and I wanted to return the inserted entities (including any generated cols such as ids)
    – steff_bdh
    Commented Jul 22, 2020 at 12:45
  • Good, but doesn't include attributes that are defined as relationships to other tables Commented Sep 5, 2022 at 8:49
73

rows have an _asdict() function which gives a 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'}
6
  • It is supposed to be private and not could possibly be removed/changed in future versions.
    – balki
    Commented May 3, 2017 at 23:08
  • 8
    @balki It is quite well documented and as such not quite private. Though a leading underscore has that meaning in Python in general, here it is probably used in order to not clash with possible tuple keys. Commented Aug 17, 2017 at 8:59
  • 12
    This only works with KeyedTuple s, which are only returned when querying specific fields of a row. ie .query(Topic.name) returns a KeyedTuple, while .query(Topic) returns a Topic, which does not have ._asdict() - Derp. just saw STBs answer below.
    – Chad Lowe
    Commented Nov 10, 2017 at 19:50
  • KeyedTuple has been replaced with engine.Row in 1.4
    – Josh
    Commented Aug 20, 2021 at 21:10
  • _asdict() solved my problem
    – Huhu
    Commented Oct 3, 2023 at 15:40
35

as @balki mentioned:

The _asdict() method can be used if you're querying a specific field because it is returned as a KeyedTuple.

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

Whereas, if you do not specify a column you can use one of the other proposed methods - such as the one provided by @charlax. Note that this method is only valid for 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'}
6
  • If the python ORM class attributes have different names from the database columns, try this solution: stackoverflow.com/questions/27947294/… Commented Jan 14, 2015 at 16:05
  • 3
    actually, a better solution for all cases is provided by the sqlalchemy author at stackoverflow.com/a/27948279/1023033 Commented Jan 14, 2015 at 17:15
  • When I try this I get ResultProxy object has no attribute '_asdict' Commented Jul 10, 2015 at 20:17
  • @slashdottir, are you executing your query object (calling the .first() method)?
    – Sam Bourne
    Commented Jul 5, 2016 at 18:57
  • 2
    This answer makes an invalid assumption: column names don't necessarily match attribute names – see RazerM's answer. Commented Feb 6, 2018 at 9:46
34

Assuming the following functions will be added to the class User the following will return all key-value pairs of all columns:

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

unlike the other answers all but only those attributes of the object are returned which are Column attributes at class level of the object. Therefore no _sa_instance_state or any other attribute SQLalchemy or you add to the object are included. Reference

EDIT: Forget to say, that this also works on inherited Columns.

hybrid_property extention

If you also want to include hybrid_property attributes the following will work:

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_

I assume here that you mark Columns with an beginning _ to indicate that you want to hide them, either because you access the attribute by an hybrid_property or you simply do not want to show them. Reference

Tipp all_orm_descriptors also returns hybrid_method and AssociationProxy if you also want to include them.

Remarks to other answers

Every answer (like 1, 2 ) which based on the __dict__ attribute simply returns all attributes of the object. This could be much more attributes then you want. Like I sad this includes _sa_instance_state or any other attribute you define on this object.

Every answer (like 1, 2 ) which is based on the dict() function only works on SQLalchemy row objects returned by session.execute() not on the classes you define to work with, like the class User from the question.

The solving answer which is based on row.__table__.columns will definitely not work. row.__table__.columns contains the column names of the SQL Database. These can only be equal to the attributes name of the python object. If not you get an AttributeError. For answers (like 1, 2 ) based on class_mapper(obj.__class__).mapped_table.c it is the same.

1
  • Perfect for adding a simple method to make models easily JSON serializable
    – AFOC
    Commented Jun 30, 2020 at 1:43
23

with sqlalchemy 1.4

session.execute(select(User.id, User.username)).mappings().all()
>> [{'id': 1, 'username': 'Bob'}, {'id': 2, 'username': 'Alice'}]
4
  • 3
    fyi for the unwary: you need to use select 2.0 query rather than session.query (at least for my use case), or this still not work.
    – rsmith54
    Commented Jan 21, 2022 at 20:31
  • this works great if you're selecting specific columns. if you run something like session.execute(select(User)).mappings().all() to select the whole object you get this result: [{'User': User(id=1, username='Bob')}, {'User': User(id=2, username='Alice')}] Commented Dec 7, 2022 at 0:34
  • however you can do session.execute(select('*').select_from(User)).mappings().all() and it will give you [{'id': 1, 'username': 'Bob'}, {'id': 2, 'username': 'Alice'}]. or if you filter the results, for example: session.execute(select('*').filter(User.id.in_([1, 2]))).mappings().all(), should get you the same result. just need a way to specify which table you're selecting '*' from. Commented Dec 7, 2022 at 0:58
  • This only works if you select specific columns.
    – bfontaine
    Commented Apr 21, 2023 at 16:27
22

Old question, but since this the first result for "sqlalchemy row to dict" in Google it deserves a better answer.

The RowProxy object that SqlAlchemy returns has the items() method: http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

It simply returns a list of (key, value) tuples. So one can convert a row to dict using the following:

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]
4
  • 18
    You can just do list_of_dicts = [dict(row.items()) for row in rows]
    – Mahi
    Commented Nov 7, 2016 at 11:03
  • One snag is that the column names that SQLAlchemy uses in a result set are table_name_column_name, if you want different names (eg. just column_name), use the .label method. session.query( MyTable.column_name.label('column_name'), ... )
    – Aneel
    Commented Apr 22, 2018 at 2:01
  • Hi I am getting this issue pls help me * datetime.datetime(2018, 11, 24, 18, 52, 50) is not JSON serializable * Commented Nov 28, 2018 at 9:26
  • 4
    It seems that Row.items() disappeared in SQLAlchemy 1.4. If you were using it in SQLAlchemy 1.3, you will need to change to dict(row).items()
    – remram
    Commented Jul 21, 2021 at 18:12
20

A very simple solution is row._asdict():

data = session.query(Table).all()
[row._asdict() for row in data]

References:

4
  • 4
    At this moment it is not in the docs. Maybe it is deprecated.
    – Rutrus
    Commented Feb 9, 2021 at 0:44
  • I've added links to the 1.4 and 1.3 docs. Commented Sep 24, 2021 at 20:01
  • 4
    Important: the Table mentioned here is not a Model (a class), but rather a Table instance (that can be accessed as Model.__table__. The model (the most common way of declaring tables) does not have this attribute. Commented Dec 10, 2022 at 5:20
  • Thanks for this note @lowercase00 so in the example above the query would then be: data = session.query(Model.__table__).all() and then each instance will have the _asdict attribute Commented May 17, 2023 at 20:36
14

Following @balki answer, since SQLAlchemy 0.8 you can use _asdict(), available for KeyedTuple objects. This renders a pretty straightforward answer to the original question. Just, change in your example the last two lines (the for loop) for this one:

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

This works because in the above code u is an object of type class KeyedTuple, since .all() returns a list of KeyedTuple. Therefore it has the method _asdict(), which nicely returns u as a dictionary.

WRT the answer by @STB: AFAIK, anything that .all() returns is a list of KeypedTuple. Therefore, the above works either if you specify a column or not, as long as you are dealing with the result of .all() as applied to a Query object.

4
  • 7
    This may have been true in the past, but on SQLAlchemy v1.0 .all() returns a list of User instances, so this doesn't work.
    – RazerM
    Commented May 20, 2016 at 15:42
  • @RazerM, sorry, but I don't understand what you mean. The for loop should precisely loop through the list of User instances, converting them (u) to dictionaries, and then printing them...
    – jgbarah
    Commented May 25, 2016 at 21:00
  • 4
    User instances don't have an _asdict method. See gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d
    – RazerM
    Commented May 25, 2016 at 21:08
  • 2
    Now I got it. Thanks. Instead of KeyedTuple, now .all() returns User objects. So the problem for v1.0 (and up, I assume) is how to get a dictionary out of a User object. Thanks for the clarification.
    – jgbarah
    Commented May 28, 2016 at 16:36
13

With python 3.8+, we can do this with dataclass, and the asdict method that comes with it:

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 = '[email protected]'


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, [email protected]

query_result_dict = asdict(query_result)
print(query_result_dict)
# {'id': 1, 'name': 'anurag'}

The key is to use the @dataclass decorator, and annotate each column with its type (the : str part of the name: str = Column(String) line).

Also note that since the email is not annotated, it is not included in query_result_dict.

6
  • On Python3.7 I get "NameError: name 'asdict' is not defined" Commented Mar 13, 2020 at 11:31
  • My bad! It's a function added in python 3.8. Fixed my answer.
    – toaruScar
    Commented Mar 14, 2020 at 13:42
  • So pythonic. 3.8 is awesome. But you don't really need the init method do you? declarative and dataclass both provide generic init methods. Commented Jun 25, 2020 at 0:18
  • @JeffLaughlin It's not needed, but I was just being loyal to OP's code, and also wanted to provide a way to add default value to email field.
    – toaruScar
    Commented Jul 19, 2020 at 18:11
  • 1
    I don't think this is the correct way to use dataclasses with sqlalchemy. See: docs.sqlalchemy.org/en/20/orm/… But using dataclasses.asdict is indeed the best way to do it. Commented Feb 23, 2024 at 19:02
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)
3
  • 4
    Be aware of the difference between local_table and mapped_table. For example, if you apply some sort of table inheritance in your db (tbl_employees > tbl_managers, tbl_employees > tbl_staff), your mapped classes will need to reflect this (Manager(Employee), Staff(Employee)). mapped_table.c will give you the column names of both the base table and the inheriting table. local_table only gives you the name of your (inheriting) table. Commented Jul 13, 2012 at 21:49
  • This avoids giving the '_sa_instance_state' field, at least in version 0.8+. Commented Aug 15, 2013 at 20:22
  • 4
    This answer makes an invalid assumption: column names don't necessarily match attribute names.
    – RazerM
    Commented May 20, 2016 at 15:53
12

Refer to Alex Brasetvik's Answer, you can use one line of code to solve the problem

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

Under the comment section of Alex Brasetvik's Answer, zzzeek the creator of SQLAlchemy stated this is the "Correct Method" for the problem.

2
  • 1
    @Greenonline Sure, the approval comment is under the Alex Brasetvik's answer. Edited to added link to his answer
    – NorWay
    Commented Jul 4, 2017 at 7:14
  • 1
    What is the resultproxy ?
    – lameei
    Commented May 30, 2019 at 12:29
10

I've found this post because I was looking for a way to convert a SQLAlchemy row into a dict. I'm using SqlSoup... but the answer was built by myself, so, if it could helps someone here's my two cents:

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

# and now, finally...
dict(zip(c.keys(), c.values()))
3
  • 1
    or, if you prefer..: [ dict(zip(i.keys(), i.values())) for i in b ]
    – Mychot sad
    Commented Aug 4, 2012 at 16:38
  • This is the only syntax I've found that actually works! I've been trying stuff for over an hour. Commented Jul 10, 2015 at 20:24
  • For core selects, the RowProxy (c in this answer) adheres to the mapping protocol, so you can just call dict(c).
    – RazerM
    Commented May 20, 2016 at 15:56
9

You could try to do it in this way.

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

It use a built-in method in the query object that return a dictonary object of the query object.

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

3
  • 2
    Add some more explaining maybe?
    – Tyl
    Commented Jan 21, 2019 at 4:39
  • 1
    Nothing really more to explain. It's a built-in method on the result object. So whether you do this for all results, or a single row, there is a built-in _asdict() method that essentially zips the field names with field values and returns the result as a dictionary.
    – Matthew
    Commented Jul 17, 2019 at 19:33
  • Very concise and I wish it worked but u in my case is a string, and I get error ``Model' object has no attribute '_asdict'` @hllau below worked for me
    – Mote Zart
    Commented Jul 26, 2019 at 21:41
8

The expression you are iterating through evaluates to list of model objects, not rows. So the following is correct usage of them:

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

Do you realy need to convert them to dicts? Sure, there is a lot of ways, but then you don't need ORM part of SQLAlchemy:

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

Update: Take a look at sqlalchemy.orm.attributes module. It has a set of functions to work with object state, that might be useful for you, especially instance_dict().

2
  • 2
    I want to convert them to dict to, because some other code needs data as dict, and i want a generic way because I will not know what columns a model object have Commented Dec 25, 2009 at 5:56
  • and when I get handle to them I have access to model objects only so i can't use session.execute etc Commented Dec 25, 2009 at 5:57
6

I've just been dealing with this issue for a few minutes. The answer marked as correct doesn't respect the type of the fields. Solution comes from dictalchemy adding some interesting fetures. https://pythonhosted.org/dictalchemy/ I've just tested it and works fine.

Base = declarative_base(cls=DictableModel)

session.query(User).asdict()
{'id': 1, 'username': 'Gerald'}

session.query(User).asdict(exclude=['id'])
{'username': 'Gerald'}
2
  • This should be the new best sollution. How lucky that I checked every answer found this one! No more '_sa_instance_state' to be delt with.
    – robinfang
    Commented Apr 8, 2021 at 8:08
  • where does DictableModel come from?
    – Gigino
    Commented Oct 16, 2024 at 9:54
5
class User(object):
    def to_dict(self):
        return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])

That should work.

2
  • 1
    what happens if column name starts with "_" ? Commented Feb 11, 2010 at 15:54
  • 5
    I would imagine that you really shouldn't name your columns with a leading underscore. If you do, it won't work. If it's just the odd one, that you know about, you could modify it to add those columns. Commented Feb 12, 2010 at 23:29
4

You can convert sqlalchemy object to dictionary like this and return it as json/dictionary.

Helper functions:

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) 

Driver Function:

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

Two ways:

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

As OP stated, calling the dict initializer raises an exception with the message "User" object is not iterable. So the real question is how to make a SQLAlchemy Model iterable?

We'll have to implement the special methods __iter__ and __next__, but if we inherit directly from the declarative_base model, we would still run into the undesirable "_sa_instance_state" key. What's worse, is we would have to loop through __dict__.keys() for every call to __next__ because the keys() method returns a View -- an iterable that is not indexed. This would increase the time complexity by a factor of N, where N is the number of keys in __dict__. Generating the dict would cost O(N^2). We can do better.

We can implement our own Base class that implements the required special methods and stores a list of of the column names that can be accessed by index, reducing the time complexity of generating the dict to O(N). This has the added benefit that we can define the logic once and inherit from our Base class anytime we want our model class to be iterable.

class IterableBase(declarative_base()):
    __abstract__ = True

    def _init_keys(self):
        self._keys = [c.name for c in self.__table__.columns]
        self._dict = {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._init_keys()

    def __setattr__(self, name, value):
        super().__setattr__(name, value)
        if name not in ('_dict', '_keys', '_n') and '_dict' in self.__dict__:
            self._dict[name] = value

    def __iter__(self):
        self._n = 0
        return self

    def __next__(self):
        if self._n >= len(self._keys):
            raise StopIteration
        self._n += 1
        key = self._keys[self._n-1]
        return (key, self._dict[key])

Now the User class can inherit directly from our IterableBase class.

class User(IterableBase):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

You can confirm that calling the dict function with a User instance as an argument returns the desired dictionary, sans "_sa_instance_state". You may have noticed the __setattr__ method that was declared in the IterableBase class. This ensures the _dict is updated when attributes are mutated or set after initialization.

def main():
    user1 = User('Bob')
    print(dict(user1))
    # outputs {'id': None, 'name': 'Bob'}
    user1.id = 42
    print(dict(user1))
    # outputs {'id': 42, 'name': 'Bob'}

if __name__ == '__main__':
    main()
1
  • This is the working version of April 2022. Using dictalchemy was my preferred method but it's unmaintained since 2015.
    – iair
    Commented Apr 3, 2022 at 21:46
2

Here is how Elixir does it. The value of this solution is that it allows recursively including the dictionary representation of 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
0
2

With this code you can also to add to your query "filter" or "join" and this work!

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

For the sake of everyone and myself, here is how I use it:

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

To complete @Anurag Uniyal 's answer, here is a method that will recursively follow relationships:

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}
2
  • in case the default for 'with_relationships' is changed to false, better pass this value through to the recursive call. ie: d[relationship.key] = to_dict(val,with_relationships) if val else None Commented Aug 2, 2019 at 10:37
  • how can I achieve the result, if I want to join the user and address table based upon address_id column and fetch all the column from user table and only id column from address table.
    – Sudhakar
    Commented May 13, 2020 at 22:14
2
from copy import copy

def to_record(row):
    record = copy(row.__dict__)
    del record["_sa_instance_state"]
    return record

If not using copy, you might run into errors.

2

SQlAlchemy 2.x supports dataclasses. Based on the example of the original documentation:

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import MappedAsDataclass


class Base(MappedAsDataclass, DeclarativeBase):
    """subclasses will be converted to dataclasses"""


class User(Base):
    __tablename__ = "user_account"

    id: Mapped[int] = mapped_column(init=False, primary_key=True)
    name: Mapped[str]

you can apply dataclasses.asdict to instances of User:

import dataclasses

print(dataclasses.asdict(User(id=1,name='foo')))
# {'id': 1,'name': 'foo'}
1

I have a variation on Marco Mariani's answer, expressed as a decorator. The main difference is that it'll handle lists of entities, as well as safely ignoring some other types of return values (which is very useful when writing tests using mocks):

@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

Return the contents of this :class:.KeyedTuple as a dictionary

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.