将sqlalchemy行对象转换为python dict

时间:2009-12-24 12:42:34

标签: 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)

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

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

38 个答案:

答案 0 :(得分:193)

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

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

答案 1 :(得分:127)

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

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

    return d

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

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

答案 2 :(得分:64)

在SQLAlchemy v0.8及更高版本中,使用inspection system

$container = new \Slim\Container();
$container['customError'] = function($c){
    return function ($request, $response) use ($c) {
        $output = ['success'=>0, 'error'=>"Custom Error Output."];
        return $c['response']
            ->withStatus(400)
            ->withHeader('Content-Type', 'application/json')
            ->write(json_encode($output));
    };
};
$app = new \Slim\App($container);

请注意,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

此方法也适用于column_property

答案 3 :(得分:63)

for row in resultproxy:
    row_as_dict = dict(row)

答案 4 :(得分:32)

行有_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'}

答案 5 :(得分:17)

正如@balki所说:

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

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

然而,如果您没有指定列,则可以使用其他建议方法之一 - 例如@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'}

答案 6 :(得分:14)

老问题,但由于这是Google中“sqlalchemy row to dict”的第一个结果,它值得一个更好的答案。

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

它只返回(键,值)元组的列表。因此可以使用以下内容将行转换为dict:

在Python&lt; = 2.6:

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

在Python中&gt; = 2.7:

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

答案 7 :(得分: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)

答案 8 :(得分:9)

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

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

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

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

答案 9 :(得分:9)

假设以下函数将添加到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 或您添加到对象。 Reference

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

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_

我在此假设您使用开头_标记列,以指示您要隐藏它们,因为您通过hybrid_property访问该属性,或者您根本不想显示它们。 Reference

Tipp all_orm_descriptors如果您还要包含它们,也会返回hybrid_methodAssociationProxy

对其他答案的评论

基于__dict__属性的每个答案(如12)只返回对象的所有属性。这可能是您想要的更多属性。就像我很遗憾,这包括_sa_instance_state或您在此对象上定义的任何其他属性。

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

基于row.__table__.columns的{​​{3}}肯定会工作。 row.__table__.columns包含SQL数据库的列名。这些只能等于python对象的属性名称。如果没有,你得到一个AttributeError。 对于基于class_mapper(obj.__class__).mapped_table.c的答案(例如solving answer1),它是相同的。

答案 10 :(得分:8)

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

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

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

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

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

答案 11 :(得分:7)

参考Alex Brasetvik's Answer,您可以使用一行代码来解决问题

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

在Alex Brasetvik的回答评论部分,SQLAlchemy的创建者zzzeek表示这是解决问题的“正确方法”。

答案 12 :(得分: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()))

答案 13 :(得分:6)

使用python 3.8+,我们可以使用数据类以及其随附的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'}

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

还请注意,由于email未被注释,因此它不包含在query_result_dict中。

答案 14 :(得分:4)

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

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

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

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

答案 15 :(得分:4)

您可以将sqlalchemy对象转换为此类字典并将其作为json / dictionary返回。

助手功能:

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) 

驱动程序功能:

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

答案 16 :(得分:3)

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

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

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

答案 17 :(得分:2)

我刚刚处理这个问题几分钟。 标记为正确的答案不尊重字段的类型。 解决方案来自 dictalchemy 添加一些有趣的特征。 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'}

答案 18 :(得分:2)

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

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

答案 19 :(得分:2)

使用此代码,您还可以添加到您的查询“过滤器”或“加入”,这项工作!

ObjectStream<DocumentSample> bin 
    = ObjectStreamUtils.createObjectStream(categoryStreams.toArray(new ObjectStream[0]));

答案 20 :(得分:2)

两种方式:

1

dst <- dist(Model_Results,method="binary") 
hca <- hclust(dst)
clust <- cutree(hca,k=40)
dend <-as.dendrogram(hca)
library(dendextend)
clust.cutree <- dendextend:::cutree(dend, k=40, order_clusters_as_data = FALSE)
idx <- order(as.numeric(names(clust.cutree)))
clust.cutree <- clust.cutree[idx]
tbl <- table(clust, clust.cutree)
lbls <- apply(tbl,2,which.max)
dend1 <- color_branches(dend, k = 40, groupLabels = lbls)
plot(dend1)

2

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

答案 21 :(得分:1)

我们可以获得字典中的对象列表:

def queryset_to_dict(query_result):
   query_columns = query_result[0].keys()
   res = [list(ele) for ele in query_result]
   dict_list = [dict(zip(query_columns, l)) for l in res]
   return dict_list

query_result = db.session.query(LanguageMaster).all()
dictvalue=queryset_to_dict(query_result)

答案 22 :(得分:1)

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

@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

答案 23 :(得分:1)

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

这应该有效。

答案 24 :(得分:0)

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

宁可尝试这个, 在SQLAlchemy中继承基本查询类

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”方法,都将在那里。

答案 25 :(得分:0)

要完成@Anurag Uniyal的回答,以下是一种将递归地遵循关系的方法:

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}

答案 26 :(得分:0)

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))

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

答案 27 :(得分:0)

使用字典推导

for u in session.query(User).all():
    print ({column.name: str(getattr(row, column.name)) for column in row.__table__.columns})

答案 28 :(得分:0)

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

import Foundation
import MapKit

class CustomPin: NSObject, MKAnnotation
{     
    var coordinate: CLLocationCoordinate2D
    var title: String?

    init(pinTitle: String, location: CLLocationCoordinate2D) {
        self.title = pinTitle
        self.coordinate = location
    }
}

func setPin(location: CLLocationCoordinate2D)
{
    let pin = CustomPin(pinTitle:"" ,location: location)

    self.mapView.addAnnotation(pin)
    self.mapView.delegate = self
    print(location.latitude)
    print(location.longitude)
}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? 
{   
    let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "customAnnotation")
    annotationView.image = UIImage(named: "marker")
    annotationView.canShowCallout = true
    annotationView.detailCalloutAccessoryView = addressLabel

    return annotationView
}

答案 29 :(得分:0)

以字典的形式返回此:class:DefaultHttpClient httpClient = new DefaultHttpClient(); httpClient.getParams().setParameter(HttpConnectionParams.CONNECTION_TIMEOUT, connectionTimeout); ... // some more other setting. httpClient.getParams().setParameter(HttpProtocolParams.HTTP_CONTENT_CHARSET, contentCharset); 的内容

httpClient = HttpClientBuilder.create().build();
Builder builder = RequestConfig.custom();
builder.setConnectionRequestTimeout(connectionTimeout);
... // some other setting
// no function to set the content charset

答案 30 :(得分:0)

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

例如:

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())

需要使用:

 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

{'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中的列存储不相同

答案 31 :(得分:0)

我是一名新兴的Python程序员,遇到了使用Joined表获取JSON的问题。使用这里的答案中的信息,我构建了一个函数,将合理的结果返回给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)

答案 32 :(得分:-1)

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

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])}

答案 33 :(得分:-1)

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

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

also i write the answer here

答案 34 :(得分:-2)

Python 3.6.8 +

内置的str()方法自动将datetime.datetime对象转换为iso-8806-1。

print(json.dumps([dict(row.items()) for row in rows], default=str, indent="  "))

注意:default函数仅在发生错误时才应用于值,因此不会转换intfloat值...除非出现错误:)

答案 35 :(得分:-2)

我对此没有多少经验,但以下似乎对我正在做的事情有用:

dict(row)

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

答案 36 :(得分:-2)

这是一种超级简单的方法

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

答案 37 :(得分:-3)

我的利用(太多?)词典:

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)