代码之家  ›  专栏  ›  技术社区  ›  Draemon

如何返回sqlalchemy查询中相关实体的计数

  •  10
  • Draemon  · 技术社区  · 15 年前

    我对sqlalchemy还不熟悉,虽然文档看起来相当全面,但我找不到一种方法来做我想做的事情。

    假设我有两张桌子:论坛和帖子。每个论坛都有一个父论坛和任意数量的帖子。我想要的是:

    • 顶级论坛列表
    • 通过顶级论坛访问的急切加载的儿童论坛
    • 每个职位的数量 小孩 论坛

    所以我开始说:

     query(Forum).filter(Forum.parent==None).all()
    

    这给了我所有的顶级论坛。当然,访问子论坛会产生n个select查询。

     query(Forum).options(eagerload('children')).filter(Forum.parent==None).all()
    

    这解决了n选择问题。

    现在我最好的猜测是这样的:

     query(Forum, func.count(Forum.children.posts)).options(eagerload('children')).filter(Forum.parent==None).group_by(Forum.children.id).all()
    

    但我得到的是:

    AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object has an attribute 'posts'
    

    我试过一些变化,但没有进一步的。为了清楚起见,我正在寻找与此SQL等价的SQL:

    select Forum.*, Child.*, count(Post.id)
    from Forum
    left join Forum Child on Child.parent = Forum.id
    left join Message on Message.forum = Child.id
    where Forum.parent is null
    group by Child.id
    
    1 回复  |  直到 15 年前
        1
  •  8
  •   Ants Aasma    15 年前

    因为您希望在子论坛对象上可以访问post计数,所以在设置映射程序时需要将其声明为column属性。列属性声明应该如下所示(假设使用声明式):

    Forum.post_count = column_property(select([func.count()],
            Message.__table__.c.forum == Forum.__table__.c.id
        ).correlate(Forum.__table__).as_scalar().label('post_count'),
        deferred=True)
    

    然后您可以这样表达查询:

    query(Forum).filter_by(parent=None).options(
        eagerload('children'),
        undefer('children.post_count'))
    

    另一个选择是选择子项并分别计数。在这种情况下,您需要自己对结果进行分组:

    ChildForum = aliased(Forum)
    q = (query(Forum, ChildForum, func.count(Message.id))
            .filter(Forum.parent == None)
            .outerjoin((ChildForum, Forum.children))
            .outerjoin(ChildForum.posts)
            .group_by(Forum, ChildForum)
        )
    
    from itertools import groupby
    from operator import attrgetter
    
    for forum, childforums in groupby(q, key=attrgetter('Node')):
        for _, child, post_count in childforums:
            if child is None:
                # No children
                break
            # do something with child