2017-04-26 134 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