【问题标题】:Complex aggregation in DjangoDjango中的复杂聚合
【发布时间】:2017-09-23 12:19:57
【问题描述】:

使用 Django rest framework 3.x 和Django 1.1.10。我有一个代表用户的模型。当我通过访问 DRF 中的/users/ 端点列出所有用户时,列表必须包含更多通过另一个模型(称为所有者)与用户相关的数据。每个项目都有一个所有者,所有者有用户。

我在 User 模型上创建了一个额外的属性,它只返回一个 JSON 数据数组。这是我无法改变的,因为这是前端的要求。我必须返回与每个用户相关的项目总数,并且要执行三种不同的计数来获取数据。

我需要在同一模型上获取多个 count() 的项目,但条件不同。

分别做这些很容易,两个微不足道,最后一个更复杂:

Item.objects.filter(owner__user=self).count()
Item.objects.filter(owner__user=self, published=True).count()
Item.objects.filter(Q(history__action__name='argle') | Q(history__action__name='bargle'),
                    history__since__lte=now,
                    history__until__gte=now,
                    owner__user=self).count()

问题是因为这是为每个用户运行的,而且其中有很多用户。最后,这会生成 300 多个数据库查询,我希望将这些查询降至最低。

到目前为止,我想出了这个:

Item.objects.filter(owner__user=self)\
            .aggregate(published=Count('published'),
                       total=Count('id'))

这将聚合前两个计数,返回它们,并且只会在数据库上执行一个SELECT。有没有办法将最后一个 count() 调用合并到同一个 aggregate() 中?

我尝试了很多东西,但似乎不可能。我应该只写一个自定义的SELECT 并使用Item.objects.raw()吗?

我还注意到,在我的开发机器和 SQLite 上执行 aggregate() 和最后一个 count() 比在带有 Postgresql 的登台服务器上更快,这有点奇怪,但这不是我现在主要关心的问题。

【问题讨论】:

    标签: python django django-models aggregation django-annotate


    【解决方案1】:

    由于您需要 QuerySet 中每个项目的计数,因此您应该使用 annotate 而不是聚合,这样只会执行 1 个查询。

    根据条件计算相关对象的最佳方法是使用conditional aggregation

    User.objects.annotate(
        total_items=Count('items'),
        published=Sum(Case(When(items__published=True, then=1), output_field=IntegerField())),
        foo=Sum(Case(When(
            Q(history__action__name='argle') | Q(history__action__name='bargle'),
            history__since__lte=now,
            history__until__gte=now,
            then=1
        ), output_field=IntegerField()))
    )
    

    【讨论】:

    • 哦,这太棒了。是的,用项目计数注释每个用户是要走的路。我太专注于物品本身。谢谢!
    猜你喜欢
    • 2021-01-20
    • 1970-01-01
    • 2012-06-09
    • 2014-10-21
    • 2019-11-04
    • 2018-10-31
    • 2019-06-06
    • 2019-03-14
    • 2021-04-06
    相关资源
    最近更新 更多