将sqlalchemy行对象转换为python字典
是否有一个简单的方法来迭代列名称和值对?
我的sqlalchemy版本是0.5.6
这里是我尝试使用字典(行)的示例代码,但它会抛出exception,TypeError:'用户'对象不可迭代
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)
在我的系统输出上运行这段代码:
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__
,如下所示:
for u in session.query(User).all(): print u.__dict__
我无法得到一个好的答案,所以我使用这个:
def row2dict(row): d = {} for column in row.__table__.columns: d[column.name] = str(getattr(row, column.name)) return d
编辑:如果上面的function太长,不适合一些口味这里是一个class轮(python2.7 +)
row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
for row in resultproxy: row_as_dict = dict(row)
这是现代SQLAlchemy(v0.8 – v1.2 +)的正确答案。
使用检查系统 。
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
是属性名称,可能与列名称不同,例如在以下情况下:
class_ = Column('class', Text)
这个方法也适用于column_property
。
行有一个_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'}
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)
作为@balki提到:
如果您查询特定的字段,则可以使用_asdict()
方法,因为它作为KeyedTuple返回。
In [1]: foo = db.session.query(Topic.name).first() In [2]: foo._asdict() Out[2]: {'name': u'blah'}
而如果不指定列,则可以使用其他build议的方法之一 – 例如由@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'}
在@balki回答之后, 由于SQLAlchemy 0.8可以使用_asdict(),可用于KeyedTuple对象。 这对原来的问题提出了一个非常简单的答案。 只是,在你的例子中改变了这个最后两行(for循环):
for u in session.query(User).all(): print u._asdict()
这是可行的 ,因为在上面的代码中,u是类KeyedTuple的对象,因为.all ()返回KeyedTuple的列表。 因此,它有_asdict()方法,很好地返回你作为一个字典。
WRT答案由@STB:AFAIK,anithong .all()返回是一个KeypedTuple列表。 因此,只要你处理应用于查询对象的.all()的结果,上面的工作或者是指定一个列。
老问题,但是因为这是Google的“sqlalchemy row to dict”的第一个结果,所以应该有更好的答案。
SqlAlchemy返回的RowProxy对象具有items()方法: http ://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items
它只是返回(键,值)元组列表。 所以可以使用以下代码将行转换为字典:
在Python中<= 2.6:
rows = conn.execute(query) list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows]
在Python> = 2.7:
rows = conn.execute(query) list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows]
我find了这篇文章,因为我正在寻找一种将SQLAlchemy行转换为字典的方法。 我正在使用SqlSoup …但答案是由我自己构build的,所以,如果能帮助别人,这里是我的两分钱:
a = db.execute('select * from acquisizioni_motes') b = a.fetchall() c = b[0] # and now, finally... dict(zip(c.keys(), c.values()))
您正在迭代的expression式计算到模型对象列表,而不是行。 所以下面是他们的正确用法:
for u in session.query(User).all(): print u.id, u.name
你真的需要把它们转换成字典吗? 当然,有很多方法,但是你不需要SQLAlchemy的ORM部分:
result = session.execute(User.__table__.select()) for row in result: print dict(row)
更新 :看看sqlalchemy.orm.attributes
模块。 它有一组函数来处理对象状态,这可能对你有用,特别是instance_dict()
。
请参阅Alex Brasetvik的“答案” ,您可以使用一行代码来解决问题
row_as_dict = [dict(row) for row in resultproxy]
在Alex Brasetvik's Answer的注释部分,SQLAlchemy的创build者zzzeek表示这是问题的“正确方法”。
假设以下函数将被添加到class User
,以下代码将返回所有列的所有键 – 值对:
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或您添加到该对象包括在内。 参考
编辑:忘记说,这也适用于inheritance的列。
hybrid_propery
扩展
如果您还想要包含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_
我在这里假设你用开头标记Columns来指示你想要隐藏它们,要么是因为你通过hybrid_property
访问属性,或者你根本不想显示它们。 参考
Tipp的 all_orm_descriptors
也返回hybrid_method和AssociationProxy,如果你也想包括它们。
对其他答案的评论
每个基于__dict__
属性的回答(如1,2 )都会返回对象的所有属性。 这可能是更多的属性,然后你想要的。 就像我悲伤,这包括_sa_instance_state
或您在此对象上定义的任何其他属性。
基于dict()
函数的每个答案(如1,2 dict()
只能在session.execute()
返回的SQLalchemy行对象上工作,而不能在您定义的class User
,就像问题的class User
一样。
基于row.__table__.columns
的求解答案肯定不行 。 row.__table__.columns
包含SQL数据库的列名称。 这些只能等于python对象的属性名称。 如果没有,你会得到一个AttributeError
。 对于基于class_mapper(obj.__class__).mapped_table.c
答案(如1,2) ,它是一样的。
这是Elixir如何做的。 这个解决scheme的价值在于它允许recursion地包含关系的字典表示。
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
我对Marco Mariani的回答有一个变化,表示为装饰者。 主要区别在于它将处理实体列表 ,以及安全地忽略一些其他types的返回值(在使用mock编写testing时非常有用):
@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
class User(object): def to_dict(self): return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])
这应该工作。
我发现的最简单的方法(使用sqla 0.7.8):
dictlist = [dict(row) for row in somequery.execute().fetchall()]
我是一个新成立的Python程序员,并遇到与联合表格JSON的问题。 使用这里的答案的信息,我build立了一个函数来返回合理的结果给包含表名的JSON,避免了别名,或者有字段冲突。
只需传递一个会话查询的结果:
test = Session()。query(VMInfo,Customer).join(Customer).order_by(VMInfo.vm_name).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)
同样适用于inheritance类的解决scheme:
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])}
有了这个代码,你也可以添加到您的查询“filter”或“join”,这项工作!
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]
在大多数情况下,列名适合他们。 但也许你写的代码如下所示:
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
我也在这里写下答案
这是一个超级简单的方法
row2dict = lambda r: dict(r.items())
我没有太多的经验,但以下似乎为我在做什么工作:
dict(row)
这似乎太简单了(与其他答案相比)。 我错过了什么?
我利用(太多?)字典:
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
运行瓶(包括jsonify)和flask_sqlalchemy以JSON格式输出。
用jsonify(serialize())调用函数。
适用于迄今为止我尝试过的所有SQLAlchemy查询(运行SQLite3)