SQLAlchemy中与Django的annotate()方法等效的方法
我在使用SQLAlchemy做一个连接操作,代码是这样的:
items = Item.query\
.outerjoin((ItemInfo, ItemInfo.item_id==Item.id))
items.add_columns(ItemInfo.count)
这样做会导致SQLAlchemy返回的是元组,也就是一组数据:
>>> items.first()
(<Item ...>, 2)
我更希望“count”这个值能作为项目的一个属性返回,也就是说,我想这样做:
>>> items.first().count
2
这样做可以吗?
1 个回答
6
其实,"items.first().count" 是可以用的,因为你得到的是一个命名元组……不过我猜你不想看到 items.first().item.foo 这样的写法。
第二种方法是,你可以把查询的结果通过一个函数处理,来构造你想要的结果:
def process(q):
for item, count in q:
item.count = count
yield count
补充: 这里有一个更通用的版本:
from sqlalchemy.orm.query import Query
class AnnotateQuery(Query):
_annotations = ()
def annotate(self, key, expr):
q = self.add_column(expr)
q._annotations = self._annotations + (key, )
return q
def __iter__(self):
if not self._annotations:
return super(AnnotateQuery, self).__iter__()
else:
for row in super(AnnotateQuery, self):
item, remaining = row[0], row[1:]
for i, key in enumerate(self._annotations):
setattr(item, key, remaining[i])
yield item
# session usage:
Session = sessionmaker(query_cls=AnnotateQuery)
# query usage:
q = Session.query(Item).outerjoin(...).annotate('count', Item.count)
第三种方法是,你可以修改 Item 类来支持这个功能。你会使用 column_property()
来给你的类应用一个选择子查询:http://www.sqlalchemy.org/docs/orm/mapper_config.html#sql-expressions-as-mapped-attributes 。如果你想让这个属性的加载是有条件的,你可以使用 deferred:http://www.sqlalchemy.org/docs/orm/mapper_config.html#deferred-column-loading 。