2017-04-26 92 views
1

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

我在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的临时服务器上更快,这有点奇怪,但它现在不是我主要关心的问题。

回答

1

由于您需要查询集中每个项目的计数,因此您应该使用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())) 
) 
+0

哦,这是辉煌的。是的,使用物品数量注释每个用户是要走的路。我太专注于物品本身。谢谢! – BigWhale