将sqlalchemy行对象转换为python dict

Convert sqlalchemy row object to python dict

有没有一种简单的方法可以遍历列名和值对?

我的sqlalchemy版本是0.5.6

这是我尝试使用dict(row)的示例代码,但是它抛出异常,TypeError:'User'对象不可迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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)

在我的系统输出上运行以下代码:

1
2
3
4
5
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


您可以访问SQLAlchemy对象的内部__dict__,如下所示:

1
2
for u in session.query(User).all():
    print u.__dict__


我无法得到一个很好的答案,所以我用这个:

1
2
3
4
5
6
def row2dict(row):
    d = {}
    for column in row.__table__.columns:
        d[column.name] = str(getattr(row, column.name))

    return d

编辑:如果以上功能太长,不适合某些口味,这里是一个衬板(python 2.7+)

1
row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}


按照@zzzeek的评论:

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

1
2
for row in resultproxy:
    row_as_dict = dict(row)


在SQLAlchemy v0.8及更高版本中,使用检查系统。

1
2
3
4
5
6
7
8
9
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)

请注意,.key是属性名称,可以与列名称不同,例如在以下情况下:

1
class_ = Column('class', Text)

此方法也适用于column_property


行有一个_asdict()函数,该函数给出一个字典

1
2
3
4
5
6
7
8
9
10
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'}


正如@balki所提到的:

如果要查询特定字段,可以使用_asdict()方法,因为它作为KeyedTuple返回。

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

而如果不指定列,则可以使用其他建议的方法之一,例如@charlax提供的方法。请注意,此方法仅对2.7+有效。

1
2
3
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'}


这是一个古老的问题,但是由于这是Google中" sqlalchemy dict的第一个结果"的结果,因此它应该有一个更好的答案。

SqlAlchemy返回的RowProxy对象具有items()方法:
http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items

它仅返回(键,值)元组的列表。因此,可以使用以下命令将行转换为字典:

在Python <= 2.6中:

1
2
rows = conn.execute(query)
list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows]

在Python> = 2.7中:

1
2
rows = conn.execute(query)
list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows]


1
2
3
4
5
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)


假设以下功能将添加到class User中,则以下内容将返回所有列的所有键值对:

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

与其他答案不同的是,仅返回对象的那些属性,而这些属性只是对象类级别的Column属性。因此,不包含_sa_instance_state或任何其他SQLalchemy属性或您添加到该对象的属性。参考

编辑:不用说,这也适用于继承的列。

hybrid_propery扩展

如果您还想包含hybrid_property属性,则可以进行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
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_

我在这里假设您以" _"开头标记"列",以表示您要隐藏它们,这是因为您通过" hybrid_property"访问属性,或者只是不想显示它们。参考

如果您还想将它们包括在内,则Tipp all_orm_descriptors也将返回hybrid_method和AssociationProxy。

对其他答案的评论

基于__dict__属性的每个答案(如1、2)仅返回对象的所有属性。这可能是您想要的更多属性。就像我难过一样,它包括_sa_instance_state或您在此对象上定义的任何其他属性。

每个基于dict()函数的答案(如1、2)仅适用于session.execute()返回的SQLalchemy行对象,而不适用于您定义要使用的类,例如问题中的class User

基于row.__table__.columns的解决方案肯定不会起作用。 row.__table__.columns包含SQL数据库的列名。这些只能等于python对象的属性名称。如果没有,您将得到一个AttributeError
对于基于class_mapper(obj.__class__).mapped_table.c的答案(如1、2),它是相同的。


在@balki回答之后,由于SQLAlchemy 0.8,您可以使用_asdict()(可用于KeyedTuple对象)。这为原始问题提供了一个非常简单的答案。只是,在您的示例中更改此行的最后两行(for循环):

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

之所以有效,是因为在上面的代码中,u是KeyedTuple类类型的对象,因为.all()返回KeyedTuple的列表。因此,它具有方法_asdict(),该方法很好地将u作为字典返回。

用@STB WRT回答:AFAIK,.all()返回的anithong是KeypedTuple的列表。因此,只要您处理的是应用于查询对象的.all()结果,则无论您是否指定列,上述方法均有效。


您要遍历的表达式将得出模型对象而不是行的列表。因此,以下是它们的正确用法:

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

您真的需要将它们转换为字典吗?当然,有很多方法,但是您不需要SQLAlchemy的ORM部分:

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

更新:看一下sqlalchemy.orm.attributes模块。它具有一组处理对象状态的功能,这可能对您有用,尤其是instance_dict()


我找到这篇文章是因为我正在寻找一种将SQLAlchemy行转换为dict的方法。我正在使用SqlSoup ...,但是答案是我自己建立的,因此,如果可以帮助某人,这是我的两分钱:

1
2
3
4
5
6
a = db.execute('select * from acquisizioni_motes')
b = a.fetchall()
c = b[0]

# and now, finally...
dict(zip(c.keys(), c.values()))


参考Alex Brasetvik的答案,您可以使用一行代码来解决问题

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

在Alex Brasetvik的Answer的评论部分下,SQLAlchemy的创建者zzzeek表示这是该问题的"正确方法"。


您可以尝试以这种方式进行。

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

它在查询对象中使用内置方法,该方法返回查询对象的字典对象。

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


您可以像这样将sqlalchemy对象转换为字典,并将其返回为json / dictionary。

辅助功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)

驱动功能:

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

两种方式:

1。

1
2
for row in session.execute(session.query(User).statement):
    print(dict(row))

2。

1
2
3
4
selected_columns = User.__table__.columns
rows = session.query(User).with_entities(*selected_columns).all()
for row in rows :
    print(row._asdict())

该文档提供了一个非常简单的解决方案:ResultRow._asdict()

1
2
3
4
5
6
def to_array(rows):
    return [r._asdict() for r in rows]

def query():
    data = session.query(Table).all()
    return to_array(data)

这就是Elixir的做法。该解决方案的价值在于它允许递归地包括关系的字典表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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


使用此代码,您还可以将"过滤器"或"连接"添加到查询中,从而完成这项工作!

1
2
3
4
5
6
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]


我对马可·马里亚尼(Marco Mariani)的回答有所不同,表示为装饰者。主要区别在于它将处理实体列表,并安全地忽略其他类型的返回值(这在使用模拟编写测试时非常有用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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
2
3
class User(object):
    def to_dict(self):
        return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])

那应该工作。


为了完成@Anurag Uniyal的回答,这是一种将递归地遵循关系的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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}


我是一位刚入门的Python程序员,在使用Joined表访问JSON时遇到问题。使用此处答案的信息,我构建了一个函数,用于将合理的结果返回到JSON,其中包含表名,从而避免了别名或字段冲突。

只需传递会话查询的结果即可:

测试= Session()。query(VMInfo,Customer).join(Customer).order_by(VMInfo.vm_name).limit(50).offset(10)

json = sqlAl2json(测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)

您在项目中的任何地方都需要它,我非常感谢@anurag回答说它很好用。到现在为止,我一直在使用它,但是它将弄乱您的所有代码,并且也将无法进行实体更改。

而是尝试一下
在SQLAlchemy中继承您的基本查询类

1
2
3
4
5
6
7
8
9
10
11
12
13
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)

之后,无论您在何处定义对象," as_dict"方法都将存在。


如果您的模型表列不等于mysql列。

如 :

1
2
3
4
5
6
7
8
9
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())

需要使用:

1
2
3
4
 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}

如果使用这种方式,则可以获取Modify_time和create_time均为None

1
2
3
4
5
6
{'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}

因为类属性名称与mysql中的列存储不相等


以字典形式返回此:class:.KeyedTuple的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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'}

为了所有人和我自己,这是我的用法:

1
2
3
4
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]

1
2
3
4
5
6
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))

此功能可能会有所帮助。
当属性名称不同于列名称时,找不到更好的解决方案。


在大多数情况下,列名适合它们。但是也许您编写如下代码:

1
2
3
class UserModel(BaseModel):
    user_id = Column("user_id", INT, primary_key=True)
    email = Column("user_email", STRING)

column.name为" user_email",而字段名称为" email"时,column.name不能像以前那样正常工作。

sqlalchemy_base_model.py

我也在这里写答案


一个也适用于继承的类的解决方案:

1
2
3
4
5
6
7
8
9
10
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
dict(row)

这似乎太简单了(与此处的其他答案相比)。我想念什么?


这是一种超级简单的方法

1
row2dict = lambda r: dict(r.items())

我使用(太多?)词典的观点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

与flask(包括jsonify)和flask_sqlalchemy一起运行,以将输出打印为JSON。

使用jsonify(serialize())调用该函数。

适用于到目前为止我尝试过的所有SQLAlchemy查询(运行SQLite3)