假設(shè)我們有如下的 Model :
class Article(models.Model):
title = models.CharField('標題', max_length=200)
body = models.TextField('正文')
created_time = models.DateTimeField('創(chuàng)建時間', auto_now_add=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='作者', on_delete=models.CASCADE)
category = models.ForeignKey('Category', verbose_name='分類', on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', verbose_name='標簽集合', blank=True)
def __str__(self):
return self.title
class Category(models.Model):
name = models.CharField('分類名', max_length=30)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField('標簽名', max_length=30)
def __str__(self):
return self.name
可以看到 Article 與 Author,Category 是外鍵關(guān)聯(lián)的,而和 Tag 多對多的關(guān)系。有時候我們有這樣的需求:在模板中顯示全部的 category,author,tag,同時還要對應(yīng)顯示與其關(guān)聯(lián)的 Article 數(shù)量,例如:
- 分類一 ( 10 )
- 分類二 ( 15 )
- 分類三 ( 8 )
由于 Article 和 Category 外鍵關(guān)聯(lián)的,在模板中我們可以使用 {{ category_instance.article_set.count }} 取得 category_instance 這一特定分類下關(guān)聯(lián)的全部 Article 數(shù)量,但是缺點也很明顯,無法在模板標簽中傳遞參數(shù),count 方法將返回全部關(guān)聯(lián)的文章數(shù)目。有時我們想要更加精細化一點的顯示,比如統(tǒng)計某個分類下全部 Article 發(fā)表時間在某個時間點后的文章,而如果該分類對應(yīng)的文章數(shù)為 0 ,我們在模板中則不顯示該分類。看上去挺復(fù)雜的邏輯,Django 的 annote 方法一句話可以解決我們的問題。
使用:
from django.db.models.aggregates import Count
category_list = Category.objects.filter(article__created_time_gt=(2015,1,1)).annotate(
num_articles=Count('article')).filter(num_articles__gt=0)
annote 的功能是根據(jù)某個規(guī)則給 queryset 中的每一個元素(例如我們這里的是 category)添加一個屬性。queryset 是從數(shù)據(jù)庫中查詢到的一組數(shù)據(jù)的集合,Django 將其封裝在 QuerySet 對象里。
這句話的意思的,首先對 Category 做篩選(filter),即篩選出其對應(yīng)的全部文章發(fā)表時間大于 2015年1月1日的 category記錄(article__created_time_gt=(2015,1,1)) ,然后為篩選出來的每一個條 category 記錄添加了一個屬性:num_articles(num_articles=Count('article'))),Count 方法為我們計算了每一條 catogory 下對應(yīng)的 article 數(shù)量,最后再對這組記錄篩選出對應(yīng)文章數(shù)量大于 0 的記錄,即滿足了我們上述要求。同理可以對 Author,Tag 做類似篩選。
值得注意的是第一個 filter 和 annotate 的順序不能亂,否則可能無法得到我們預(yù)期的結(jié)果。
相關(guān)文檔位于:QuerySet Method reference 和 Aggregation
靈活使用這些方法將使我們復(fù)雜的查詢需求代碼變得更加高效和簡短。