SQLAlchemy中与Django的annotate()方法等效的方法

6 投票
1 回答
3368 浏览
提问于 2025-04-16 06:17

我在使用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

撰写回答