Django 中的复杂聚合
Complex aggregation in Django
使用 Django 休息框架 3.x 和 Django 1.1.10
。我有一个代表用户的模型。当我通过访问 DRF 中的 /users/
端点列出所有用户时,列表必须包含更多通过另一个模型(称为 Owner)与用户相关的数据。每个项目都有一个所有者,所有者有用户。
我在用户模型上做了一个额外的 属性,它只是 return 一个 JSON 数据数组。这是我无法更改的,因为这是前端的要求。我必须 return 与每个用户相关的项目总数,并且要执行三种不同的计数来获取数据。
我需要在同一型号但条件不同的情况下获取多个 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'))
这将聚合前两个计数,return 它们并且只对数据库执行一个 SELECT
。有没有办法将最后一个 count()
调用合并到同一个 aggregate()
中?
我尝试了很多东西,但似乎不可能。我应该只写一个自定义 SELECT
并使用 Item.objects.raw()
吗?
我还注意到,在我的开发机器和 SQLite 上执行 aggregate()
和最后一个 count()
比在使用 Postgresql 的临时服务器上执行速度更快,这有点奇怪,但这不是我的现在主要关心的问题。
由于您需要 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()))
)
使用 Django 休息框架 3.x 和 Django 1.1.10
。我有一个代表用户的模型。当我通过访问 DRF 中的 /users/
端点列出所有用户时,列表必须包含更多通过另一个模型(称为 Owner)与用户相关的数据。每个项目都有一个所有者,所有者有用户。
我在用户模型上做了一个额外的 属性,它只是 return 一个 JSON 数据数组。这是我无法更改的,因为这是前端的要求。我必须 return 与每个用户相关的项目总数,并且要执行三种不同的计数来获取数据。
我需要在同一型号但条件不同的情况下获取多个 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'))
这将聚合前两个计数,return 它们并且只对数据库执行一个 SELECT
。有没有办法将最后一个 count()
调用合并到同一个 aggregate()
中?
我尝试了很多东西,但似乎不可能。我应该只写一个自定义 SELECT
并使用 Item.objects.raw()
吗?
我还注意到,在我的开发机器和 SQLite 上执行 aggregate()
和最后一个 count()
比在使用 Postgresql 的临时服务器上执行速度更快,这有点奇怪,但这不是我的现在主要关心的问题。
由于您需要 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()))
)