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对象的内部
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) |
请注意,
1 | class_ = Column('class', Text) |
此方法也适用于
行有一个
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所提到的:
如果要查询特定字段,可以使用
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) |
假设以下功能将添加到
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_ |
与其他答案不同的是,仅返回对象的那些属性,而这些属性只是对象类级别的
编辑:不用说,这也适用于继承的列。
如果您还想包含
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_ |
我在这里假设您以"
如果您还想将它们包括在内,则Tipp
对其他答案的评论
基于
每个基于
基于
对于基于
在@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行转换为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()) |
该文档提供了一个非常简单的解决方案:
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:
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)