2016-07-14 28 views
0

我有類別和項目。這些項目有一個結束字段(日期時間)。現在我需要列出所有類別,並在將來顯示相關項目數量和項目數量。舉例:如何註釋日期時間過濾的計數?

  • Cat Foo,2件物品,未來1件。
  • Cat n,n items n in the future。

該列表將很大。所以數據庫必須完成繁重的工作並註釋item_countfuture_item_count

型號:

from django.db import models 

class Cat(models.Model): 
    title = models.CharField(max_length=200) 

class Item(models.Model): 
    cat = models.ForeignKey(Cat) 
    title = models.CharField(max_length=200) 
    end = models.DateTimeField() 

創建類別和兩個相關項目。一個在過去,一個在未來:

from datetime import timedelta 
from django.utils import timezone 

cat = Cat(title='Cat 1') 
cat.save() 

item_1 = Item(cat=cat, title="Item 1", end=timezone.now() - timedelta(days=1)) 
item_1.save() 

item_2 = Item(cat=cat, title="Item 2", end=timezone.now() + timedelta(days=1)) 
item_2.save() 

當我批註ITEM_COUNT它按預期工作:

from django.db.models import Count 

Cat.objects.all().annotate(
    item_count=Count('item')).values('title', 'item_count') 
# [{'item_count': 2, 'title': u'Cat 1'}] 

我不能做批註通過Item.end(日期時間)進行過濾。這可能與Django的查詢呢?

Cat.objects.all().annotate(
    item_count=Count('item'), 
    future_item_count=Count('item').filter(
     end__gt=timezone.now()) 
    ).values(
     'title', 
     'item_count', 
     'future_item_count' 
    ) 
# AttributeError: 'Count' object has no attribute 'filter' 

我期望能獲得:[{'item_count': 2, 'future_item_count': 1, 'title': u'Cat 1'}]

我也試過RawSQL但缺少SQL技能:

from django.db.models.expressions import RawSQL 

Cat.objects.all().annotate(
    item_count=Count('item'), 
    future_item_count=RawSQL(
     """SELECT COUNT(*) 
      FROM project_item 
      JOIN project_item 
      AS foo 
      ON foo.cat_id = project_cat.id 
      WHERE project_item.end < NOW()""", 
     "" 
    )).values(
     'title', 
     'item_count', 
     'future_item_count' 
    ) 
# [{'item_count': 2, 'future_item_count': 2L, 'title': u'Cat 1'}] 

但是,當我在WHERE project_item.end > NOW()"改變WHERE project_item.end < NOW()"我得到了相同的結果:

[{'item_count': 2, 'future_item_count': 2L, 'title': u'Cat 1'}] 

如何格式化原始SQL?或者這可以通過Django查詢來完成嗎?

+0

您無法過濾「計數」。您正在尋找'Case'表達式,查看[documentation](https://docs.djangoproject.com/en/1.9/ref/models/conditional-expressions/#case) – dnaranjo

+0

@dnaranjo感謝您的建議。我確實讀過這部分文檔,但放棄了這個選項,因爲它在註釋中使用了'then'的值。也許我錯過了一些東西。雖然我接受了原始答案,但我仍然想知道如何做到這一點,而不會退回到原始SQL。 – allcaps

回答

1

我個人沒有使用RawSQL(仍然做.extra的東西),但我想你不需要JOIN project_itemRawSQL聲明。只是嘗試:

RawSQL("""SELECT COUNT(*) 
      FROM project_item 
      WHERE 
       project_item.cat_id = project_cat.id 
       AND project_item.end < NOW() 
""") 

還有一件事,我認爲你不應該使用.values.annotate,但之前註釋。因此,您的完整查詢集應如下所示:

Cat.objects.values('title')\ 
    .annotate(
     item_count=Count('item'), 
     future_item_count=RawSQL(""" 
      SELECT COUNT(*) 
      FROM project_item 
      WHERE 
       project_item.cat_id = project_cat.id 
       AND project_item.end < NOW() 
      """) 
    ) 
+0

謝謝。我會給它一個旋轉。我只爲示例代碼添加了值。它打印更好的結果。在應用程序中,我將使用完整的對象。 – allcaps

+0

我工作!大。 RawSQL需要兩個值。一個是查詢,另一個是參數。我給了一個空的字符串。 – allcaps