关于python:SQLAlchemy execute()将ResultProxy返回为Tuple,而不是dict

SQLAlchemy execute() return ResultProxy as Tuple, not dict

我有以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
query ="""
SELECT Coalesce((SELECT sp.param_value
                 FROM   sites_params sp
                 WHERE  sp.param_name = 'ci'
                        AND sp.site_id = s.id
                 ORDER  BY sp.id DESC
                 LIMIT  1), -1) AS ci
FROM   sites s
WHERE  s.deleted = 0
       AND s.id = 10

"""


site = db_session.execute(query)
# print site
# <sqlalchemy.engine.result.ResultProxy object at 0x033E63D0>

site = db_session.execute(query).fetchone()
print site  # (u'375')
print list(site) # [u'375']

为什么SQLAlchemy对于此查询返回元组而不是字典? 我想使用以下样式来访问查询结果:

1
2
print site.ci
# u'375'


您是否看过ResultProxy文档?
它准确地描述了@Gryphius和@Syed Habib M的建议,即使用site['ci']

ResultProxy不会像您所声称的那样"返回元组"-它(毫无疑问)是一个行为(例如打印)像元组但还支持类似字典访问的代理:

从文档:

Individual columns may be accessed by their integer position,
case-insensitive column name, or by schema.Column object. e.g.:

row = fetchone()

col1 = row[0] # access via integer position

col2 = row['col2'] # access via name

col3 = row[mytable.c.mycol] # access via Column object.


这是一个老问题,但今天仍然有意义。使SQL Alchemy返回字典非常有用,尤其是在使用基于RESTful的返回JSON的API时。

这是我在Python 3中使用db_session的方法:

1
2
3
4
5
6
7
8
9
resultproxy = db_session.execute(query)

d, a = {}, []
for rowproxy in resultproxy:
    # rowproxy.items() returns an array like [(key0, value0), (key1, value1)]
    for column, value in rowproxy.items():
        # build up the dictionary
        d = {**d, **{column: value}}
    a.append(d)

最终结果是,数组a现在包含字典格式的查询结果。

至于如何在SQL Alchemy中工作:

  • db_session.execute(query)返回一个ResultProxy对象
  • ResultProxy对象由RowProxy对象组成
  • RowProxy对象具有.items()方法,该方法返回键,该行中所有项目的值元组,可以在for操作中将它们解压缩为key, value

这里是一种单线替代:

1
[{column: value for column, value in rowproxy.items()} for rowproxy in resultproxy]

从文档:

class sqlalchemy.engine.RowProxy(parent, row, processors, keymap)

Proxy values from a single cursor row.

Mostly follows"ordered dictionary" behavior, mapping result values to the string-based column name, the integer position of the result in the row, as well as Column instances which can be mapped to the original Columns that produced this result set (for results that correspond to constructed SQL expressions).

has_key(key)
Return True if this RowProxy contains the given key.

items()
Return a list of tuples, each tuple containing a key/value pair.

keys()
Return the list of keys as strings represented by this RowProxy.

链接:http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items


我建立了一个简单的类,使其在我们的流程中像数据库界面一样工作。它去了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from sqlalchemy import create_engine
class DBConnection:
    def __init__(self, db_instance):
        self.db_engine = create_engine('your_database_uri_string')
        self.db_engine.connect()

    def read(self, statement):
       """Executes a read query and returns a list of dicts, whose keys are column names."""
        data = self.db_engine.execute(statement).fetchall()
        results = []

        if len(data)==0:
            return results

        # results from sqlalchemy are returned as a list of tuples; this procedure converts it into a list of dicts
        for row_number, row in enumerate(data):
            results.append({})
            for column_number, value in enumerate(row):
                results[row_number][row.keys()[column_number]] = value

        return results

这可能有助于解决OP问题。我认为他遇到的问题是,行对象仅包含列值,而不包含列名本身,这与ORM查询的情况相同,在ORM查询中,结果具有包含键和值的dict属性。

python sqlalchemy动态获取列名?


您可以使用dict(site)轻松地将每个结果行转换为字典。
如果ci列存在,则site['ci']将可用。

为了拥有site.ci(根据https://stackoverflow.com/a/22084672/487460):

1
2
3
from collections import namedtuple
Site = namedtuple('Site', site.keys())
record = Site(*site)

我更喜欢用列表理解来创建一个帮助器类和方法:

1
2
3
4
5
6
class ResultHelper():

@classmethod
    def resultproxy_to_dict_list(cls, sql_alchemy_rowset):        
        return [{tuple[0]: tuple[1] for tuple in rowproxy.items()}
                                    for rowproxy in sql_alchemy_rowset]