django annotate()的使用


https://www.zmrenwu.com/post/18/

 

博客文章通常都有分類,有時候我們會看到分類名后面還跟着該分類下的文章數量。前面我們通過學習 django 博客開發入門教程搭建了一個小博客。現在想在現有的基礎上實現統計分類下有多少篇文章,該怎么做呢?最優雅的方式就是使用 django 模型的 annotate 方法。

假設我們的 django 博客有一個 Post 和 Category 模型,分別表示文章和分類:

class Post(models.Model): title = models.CharField(max_length=70) body = models.TextField() category = models.ForeignKey('Category') def __str__(self): return self.title class Category(models.Model): name = models.CharField(max_length=100) 

我們知道從數據庫取數據都是使用模型管理器 objects 實現的。比如獲取全部分類是:Category.objects.all(),假設有一個名為 test 的分類,那么獲取該分類的方法是:Category.objects.get(name='test') 。objects 除了 all、get 等方法外,還有很多操作數據庫的方法,而其中有一個 annotate 方法,該方法正可以幫我們實現本文所關注的統計分類下的文章數量的功能。具體來說,就是如下的代碼:

from django.db.models.aggregates import Count from blog.models import Category # Count 計算分類下的文章數,其接受的參數為需要計數的模型的名稱 category_list = Category.objects.annotate(num_posts=Count('post')) 

這里 annotate 不僅從數據庫獲取了全部分類,相當於使用了 all 方法,它還幫我們為每一個分類添加了一個 num_posts 屬性,其值為該分類下的文章數,這樣我們在模板中就可以調用這個屬性,例如:

{% for category in category_list %}
  <li> <a href="{% url 'blog:category' category.pk %}"> {{ category.name }} ({{ category.num_posts }})</a> </li> {% endfor %} 

這樣顯示的效果就是分類名后跟着該分類下的文章數了。

那么 annotate 的工作原理究竟是怎么樣的呢?在 Post 模型中我們通過 ForeignKey 把 Post 和 Category 關聯了起來,這時候它們的數據庫表結構就像下面這樣:

Post 表:

id title body category_id
1 post 1 ... 1
2 post 2 ... 1
3 post 3 ... 1
4 post 4 ... 2

Category 表:

name id
category 1 1
category 2 2

這里前 3 篇文章屬於 category 1,第 4 篇文章屬於 category 2。

當 django 要查詢某篇 post 對應的分類時,比如 post 1,首先查詢到它分類的 id 為 1,然后 django 再去 Category 表找到 id 為 1 的那一行,這一行就是 post 1 對應的分類了。反過來,如果要查詢 category 1 對應的全部文章呢?category 1 在 Category 表中對應的 id 是 1,django 就在 Post 表中搜索哪些行的 category_id 為 1,發現前 3 行都是,把這些行取出來就是 category 1 下的全部文章了。同理,這里 annotate 做的事情就是把全部 Category 取出來,然后去 Post 查詢每一個 Category 對應的文章,查詢完成后做一個聚合,統計每個 Category 有多少篇文章,把這個統計數字保存到 Category 的 num_posts 屬性里(注意 Category 本身沒有這個屬性,是 Python 動態添加上去的)。

此外,annotate 方法不局限於用於本文提到的統計分類下的文章數,你也可以舉一反三,只要是兩個 model 類通過 ForeignKey 或者 ManyToMany 關聯起來,那么就可以使用 annotate 方法來統計數量。比如下面這樣一個標簽系統:

class Post(models.Model): title = models.CharField(max_length=70) body = models.TextField() Tags = models.ManyToMany('Tag') def __str__(self): return self.title class Tag(models.Model): name = models.CharField(max_length=100) 

統計標簽下的文章數:

from django.db.models.aggregates import Count from blog.models import Category # Count 計算分類下的文章數,其接受的參數為需要計數的模型的名稱 category_list = Category.objects.annotate(num_posts=Count('post')) 

關於 annotate 方法官方文檔的說明在這里:annotate。同時也建議了解了解 objects 下的其它操作數據庫的方法,以便在遇到相關問題時知道去哪里查閱。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM