将sqlalchemy行对象转换为python dict

Anu*_*yal 220 python sqlalchemy

是否有一种简单的方法来迭代列名和值对?

我的sqlalchemy版本是0.5.6

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

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)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

hll*_*lau 216

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

for u in session.query(User).all():
    print u.__dict__
Run Code Online (Sandbox Code Playgroud)

  • 这给出了额外的'_sa_instance_state'字段,至少在0.7.9版本中. (84认同)
  • 这个帖子中的最佳答案,不知道为什么其他人都在提出更复杂的解决方案. (21认同)
  • 所以这会更好:`dictret = dict(row .__ dict__); dictret.pop('_ sa_instance_state',无)` (19认同)
  • 这个答案是对的.问题甚至有'dict(u)`并且正确地说它会抛出一个'TypeError`. (11认同)
  • 这一切仍然是疯狂的。为什么 SQLAlchemy 结果不能用 jsonify 序列化? (7认同)
  • 这似乎并不理想,因为人们已经指出`__dict__`包含一个`_sa_instance_state`条目,然后必须将其删除.如果您升级到将来的版本并添加了其他属性,则可能需要返回并手动处理它们.如果你只想要列数据(例如,从查询中获取实例列表并将它们放入pandas数据帧中),那么`{col.name:getattr(self,col.name)for col in self .__ table __.columns如Anurag Uniyal所回答的那样(从评论到答案的重要修正)似乎更简洁,更容易出错. (4认同)
  • jberger,它不是列名,所以你必须在之后单独删除它. (3认同)
  • 我*几天*以来一直在寻找一种从模型中制作“dict”的方法。这是最好的答案,句号。2021 年 1 月仍然适用于 SQLAlchemy `1.3.x` (3认同)
  • 就我而言,SQLalchemy 实例 `u.__dict__` 将不会返回任何字段的内容,除非您提前显式访问该字段。 (3认同)
  • 只是另一个像lyfings的建议:一个人也可以使用```{k:v for k,v in item .__ dict __.iteritems()if if str(k).startswith("_")}```甚至更具体的lyfing评论,不仅与开始下划线.(> = Python2.7,Python3使用.items()) (2认同)
  • 我猜这不再受支持,因为我得到:`AttributeError:无法在列'__dict__'的行中定位列` (2认同)
  • 您不应该尝试直接访问`__dict__`。 (2认同)

Anu*_*yal 137

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

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

    return d
Run Code Online (Sandbox Code Playgroud)

编辑:如果上面的功能太长而且不适合某些口味这里是一个单线程(python 2.7+)

row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
Run Code Online (Sandbox Code Playgroud)

  • 更简洁地说,`return dict((col,getattr(row,col))for col in row .__ table __.columnss.keys())`. (16认同)
  • 如果我的列未分配给同名属性,该怎么办?IE,`x = Column('y',Integer,primary_key = True)`?这些解决方案都不适用于这种情况. (13认同)
  • drdaeman是对的,这里是正确的片段:`return {c.name:getattr(self,c.name)for c in self .__ table __.columns}` (13认同)
  • (对于行.__表__山坳(COL,GETATTR(行,列))columns.keys().)字典',使其真正的一个套,但我更喜欢:@argentpepper是的,你甚至可以做`row2dict =拉姆达行我的代码可读,水平短,垂直长 (4认同)
  • 警告:`__table __.columnss.keys()`将不起作用,因为`columns`字典键并不总是字符串(如`getattr`要求),但可能是各种对象,如`sqlalchemy.sql.expression._truncated_label` .使用`c.name`而不是`c`对我有用. (4认同)
  • 此答案的假设无效:列名称不一定与属性名称匹配. (3认同)
  • @RazerM 的答案更正确,因为它以正确的 *type*(整数、小数、日期时间、字符串、空值等)将值作为 *objects* 返回,而此答案将值作为 *strings* 返回。 (3认同)

Ale*_*vik 75

for row in resultproxy:
    row_as_dict = dict(row)
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这是现代版SQLAlchemy的正确答案,假设"row"是核心行对象,而不是ORM映射实例. (51认同)
  • 另请注意,zzzeek是sqlalchemy的创建者. (39认同)
  • 这个答案没有帮助,因为您没有概述“结果代理”如何或是什么? (14认同)
  • 它说"XXX对象不可迭代",我使用0.5.6,我通过session.query(Klass).filter()得到.all() (7认同)
  • 核心行对象与 ORM 映射实例之间有什么区别?这对“query(MyModel).all()”中的行不起作用:*MyModel 对象不可迭代。* (5认同)
  • 请注意,“row._mapping”返回 [RowMapping](https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.RowMapping) 而不是字典。如果你想添加键或执行其他字典操作,你需要将其转换为字典。 (3认同)

Raz*_*erM 71

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

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)
Run Code Online (Sandbox Code Playgroud)

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

class_ = Column('class', Text)
Run Code Online (Sandbox Code Playgroud)

这种方法也适用column_property.

  • "现代"是什么意思? (6认同)
  • @NguyenThanh鉴于其不存在,使用SQLAlchemy v2.0尤其令人印象深刻!最新的(测试版)版本为v1.3.0b1。 (2认同)

bal*_*lki 36

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

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'}
Run Code Online (Sandbox Code Playgroud)

  • @balki 这是[相当详细的记录](http://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.util.KeyedTuple._asdict),因此不是很私密。尽管前导下划线在 Python 中通常具有该含义,但在这里可能使用它是为了不与可能的元组键发生冲突。 (5认同)
  • 这仅适用于KeyedTuple,它仅在查询行的特定字段时返回.ie .query(Topic.name)返回一个KeyedTuple,而.query(Topic)返回一个Topic,它没有._asdict() - Derp.刚刚看到STB回答如下. (3认同)

sla*_*nic 20

使用 sqlalchemy 1.4

session.execute(select(User.id, User.username)).mappings().all()
>> [{'id': 1, 'username': 'Bob'}, {'id': 2, 'username': 'Alice'}]
Run Code Online (Sandbox Code Playgroud)

  • 仅供粗心的人参考:您需要使用 select 2.0 查询而不是 session.query (至少对于我的用例而言),否则这仍然不起作用。 (3认同)

Sam*_*rne 17

正如@balki所说:

_asdict()如果您要查询特定字段,则可以使用该方法,因为它是作为KeyedTuple返回的.

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

然而,如果您没有指定列,则可以使用其他建议方法之一 - 例如@charlax提供的方法.请注意,此方法仅适用于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'}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,sqlalchemy作者在http://stackoverflow.com/a/27948279/1023033上为所有案例提供了更好的解决方案. (2认同)
  • 这个答案做出了一个无效的假设:列名称不一定与属性名称匹配 - 请参阅 RazerM 的答案。 (2认同)

小智 15

老问题,但由于这是谷歌"sqlalchemy row to dict"的第一个结果,值得一个更好的答案.

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

它只返回一个(键,值)元组列表.所以可以使用以下代码将行转换为dict:

在Python <= 2.6中:

rows = conn.execute(query)
list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows]
Run Code Online (Sandbox Code Playgroud)

在Python> = 2.7中:

rows = conn.execute(query)
list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows]
Run Code Online (Sandbox Code Playgroud)

  • 你可以为行中的行做`list_of_dicts = [dict(row.items()) (9认同)
  • 看来 `Row.items()` 在 SQLAlchemy 1.4 中消失了。如果您在 SQLAlchemy 1.3 中使用它,则需要更改为 `dict(row).items()` (3认同)

Mar*_*ani 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)
Run Code Online (Sandbox Code Playgroud)

  • 请注意local_table和mapped_table之间的区别.例如,如果在db中应用某种表继承(tbl_employees> tbl_managers,tbl_employees> tbl_staff),则映射的类将需要反映此值(Manager(Employee),Staff(Employee)).mapped_table.c将为您提供基表和继承表的列名.local_table仅为您提供(继承)表的名称. (4认同)
  • 此答案的假设无效:列名称不一定与属性名称匹配. (3认同)

jgb*_*rah 11

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

for u in session.query(User).all():
   print u._asdict()
Run Code Online (Sandbox Code Playgroud)

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

WRST答案:@STB:AFAIK,anithong .all()返回的是KeypedTuple的列表.因此,只要您正在处理应用于Query对象的.all()的结果,如果您指定了一个列,上述方法就会起作用.

  • 这可能在过去是正确的,但在SQLAlchemy v1.0``all()`返回一个`User`实例列表,所以这不起作用. (3认同)
  • `User`实例没有`_asdict`方法.请参阅https://gist.github.com/RazerM/2eff51571b3c70e8aeecd303c2a2bc8d (2认同)
  • 现在我明白了.谢谢.而不是KeyedTuple,现在.all()返回User对象.所以v1.0(以及我假设)的问题是如何从User对象中获取字典.谢谢你的澄清. (2认同)

F.R*_*aab 11

假设将以下函数添加到以下函数中将class User返回所有列的所有键值对:

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

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

编辑:忘了说,这也适用于继承的列.

hybrid_propery extention

如果您还想包含hybrid_property属性,则以下内容将起作用:

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_
Run Code Online (Sandbox Code Playgroud)

我假设您在列中标记了一个开头_,表示您要隐藏它们,或者因为您通过某个访问属性,hybrid_property或者您根本不想显示它们.参考

如果您还想包含它们,Tippall_orm_descriptors也会返回hybrid_methodAssociationProxy.

备注其他答案

每一个答案(如1,2),其基于所述__dict__属性简单地返回该对象的所有属性.这可能是您想要的更多属性.就像我很遗憾这包含_sa_instance_state或您在此对象上定义的任何其他属性.

每一个答案(如1,2),这是基于该dict()功能仅适用于SQLAlchemy的通过返回行对象session.execute()不是您所定义的工作,像类class User的问题.

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


Den*_*ach 9

您正在迭代的表达式求值为模型对象列表,而不是行.所以以下是它们的正确用法:

for u in session.query(User).all():
    print u.id, u.name
Run Code Online (Sandbox Code Playgroud)

你真的需要把它们转换成dicts吗?当然,有很多方法,但是你不需要SQLAlchemy的ORM部分:

result = session.execute(User.__table__.select())
for row in result:
    print dict(row)
Run Code Online (Sandbox Code Playgroud)

更新:看看sqlalchemy.orm.attributes模块.它有一组用于处理对象状态的函数,尤其对您有用instance_dict().

  • 我想将它们转换为dict,因为其他一些代码需要数据作为dict,我想要一种通用的方式,因为我不知道模型对象有哪些列 (2认同)

toa*_*car 8

使用 python 3.8+,我们可以使用 dataclass 和asdict它附带的方法来做到这一点:

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 = 'hello@example.com'


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, query_result.email=hello@example.com

query_result_dict = asdict(query_result)
print(query_result_dict)
# {'id': 1, 'name': 'anurag'}
Run Code Online (Sandbox Code Playgroud)

关键是使用@dataclass装饰器,并用它的类型(行的: str部分name: str = Column(String))注释每一列。

另请注意,由于email未注释,因此不包含在query_result_dict.


Myc*_*sad 7

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

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

# and now, finally...
dict(zip(c.keys(), c.values()))
Run Code Online (Sandbox Code Playgroud)


Nor*_*Way 7

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

row_as_dict = [dict(row) for row in resultproxy]
Run Code Online (Sandbox Code Playgroud)

在Alex Brasetvik的答案的评论部分,SQLAlchemy的创建者zzzeek表示这是问题的"正确方法".


Enm*_*ina 6

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

for u in session.query(User).all():
    print(u._asdict())
Run Code Online (Sandbox Code Playgroud)

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

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


Hal*_*mil 6

我刚刚处理这个问题几分钟了。标记为正确的答案不尊重字段的类型。解决方案来自字典化学添加一些有趣的功能。 https://pythonhosted.org/dictalchemy/ 我刚刚测试过它并且工作正常。

Base = declarative_base(cls=DictableModel)

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

session.query(User).asdict(exclude=['id'])
{'username': 'Gerald'}
Run Code Online (Sandbox Code Playgroud)


Sin*_*ned 5

class User(object):
    def to_dict(self):
        return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])
Run Code Online (Sandbox Code Playgroud)

那应该有效。

  • 我想你真的不应该用前导下划线来命名你的列。如果你这样做,那就行不通了。如果这只是您知道的奇怪的一列,您可以修改它以添加这些列。 (5认同)

yel*_*int 5

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

def to_array(rows):
    return [r._asdict() for r in rows]

def query():
    data = session.query(Table).all()
    return to_array(data)
Run Code Online (Sandbox Code Playgroud)

  • 目前它不在文档中。也许它已被弃用。 (4认同)
  • 重要提示:这里提到的“Table”不是“Model”(一个类),而是一个“Table”实例(可以通过“Model.__table__”访问。模型(声明表的最常见方式)确实没有这个属性。 (3认同)