您當前的位置:首頁 > 農業

Django基礎(24): aggregate和annotate方法使用詳解與示例

作者:由 大江狗 發表于 農業時間:2018-11-27

在前面的文章和案例裡,我們從資料庫裡查詢資料一般只使用了一些初級的查詢方法比如filter()和exclude()方法。但如果查詢本身比較複雜,比如需要對查詢集(queryset)的某些欄位進行計算或進行分組計算或排序, 這時我們就需要使用更高階的aggregate和annotate方法了。小編我今天就帶你看下什麼情況下需要使用aggregate和annotate方法以及如何使用它們。本文比較抽象,但非常有用, 看不懂的可以先加入微信收藏以後多看幾遍哦。

Django基礎(24): aggregate和annotate方法使用詳解與示例

aggregate和annotate方法的使用場景

Django的aggregate和annotate方法屬於高階查詢方法,主要用於組合查詢,是Django高手們必需要熟練掌握的。當我們需要對查詢集(queryset)的某些欄位進行計算或進行先分組再計算或排序, 我們就需要使用aggregate和annotate方法了。

假如我們有如下一個模型,其中Student與Hobby(愛好)是多對多的關係。我們想要知道所有學生的平均年齡,我們常規做法一般是利用for迴圈從資料庫中把符合查詢條件的student物件一個一個取出,把他們年齡相加,然後再除以總人數。當人數非常多而我們又只需要平均年齡這條資訊時,把所有符合查詢條件的學生物件都載入記憶體後再進行計算是非常浪費資源的,效率也非常低。一個更好的方法是在資料庫層面提取查詢資料時就直接返回我們所需要的資訊。因為這個查詢涉及到對整個queryset的age欄位進行統計計算,此時django的聚合函式方法aggregate就可以幫我們大大提升查詢效率了[見後文]。

class Student(models。Model):

name = models。CharField(max_length=20)

age = models。IntegerField()

hobbies = models。ManyToManyField(Hobby)

class Hobby(models。Model):

name = models。CharField(max_length=20)

另一個例子是統計最受學生歡迎的5個愛好,常規做法是先將所有hobby物件提取出來,載入記憶體。然後利用for迴圈統計每組愛好對應的學生人數,再構建一個新的查詢集,按每組人數從大到小進行排序。這個查詢需要根據hobby先進行分組,再統計每個愛好組裡學生的數量,然後進行排序。對於這個複雜查詢, django的annotate方法一句話就可以解決問題。

aggregate()方法詳解

aggregate的中文意思是聚合, 源於SQL的聚合函式。Django的aggregate()方法作用是對一組值(比如queryset的某個欄位)進行統計計算,並以字典(Dict)格式返回統計計算結果。django的aggregate方法支援的聚合操作有AVG / COUNT / MAX / MIN /SUM 等。

我們現在來看下幾組實際使用案例。使用前別忘了import Avg, Max, Min或者Sum方法哦

from django。db。models import Avg, Max, Min

# 計算學生平均年齡, 返回字典。age和avg間是雙下劃線哦

Student。objects。all()。aggregate(Avg(‘age’))

{ ‘age__avg’: 12 }

# 學生平均年齡,返回字典。all()不是必須的。

Student。objects。aggregate(Avg(‘age’))

{ ‘age__avg: 12’ }

# 計算學生總年齡, 返回字典。

Student。objects。aggregate(Sum(‘age’))

{ ‘age__sum’: 144 }

# 學生平均年齡, 設定字典的key

Student。objects。aggregate(average_age = Avg(‘age’))

{ ‘average_age’: 12 }

# 學生最大年齡,返回字典

Student。objects。aggregate(Max(‘age’))

{ ‘age__max’: 12 }

# 同時獲取學生年齡均值, 最大值和最小值, 返回字典

Student。objects。aggregate(Avg(‘age‘), Max(’age‘), Min(‘age‘))

{ ’age__avg‘: 12, ’age__max‘: 18, ’age__min‘: 6, }

# 根據Hobby反查學生最大年齡。查詢欄位student和age間有雙下劃線哦。

Hobby。objects。aggregate(Max(’student__age‘))

{ ’student__age__max‘: 12 }

你注意到了嗎? aggregate方法返回Dict型別資料和django的內容物件(context object)是一樣的哦。你可以很輕鬆地將結果傳遞給模板, 在模板中顯示。

annotate()方法詳解

annotate的中文意思是註釋,小編我覺得是非常地詞不達意,一個更好的理解是分組(Group By)。如果你想要對資料集先進行分組然後再進行某些聚合操作或排序時,需要使用annotate方法來實現。與aggregate方法不同的是,annotate方法返回結果的不僅僅是含有統計結果的一個字典,而是包含有新增統計欄位的查詢集(queryset)。

我們接下來也看下幾個實際使用案例。

# 按學生分組,統計每個學生的愛好數量

Student。objects。annotate(Count(’hobbies‘))

返回的結果依然是Student查詢集,只不過多了hobbies__count這個欄位。如果你不喜歡這個預設名字,你當然可以對這個欄位進行自定義從而使它變得更直觀。

# 按學生分組,統計每個學生愛好數量,並自定義欄位名

Student。objects。annotate(hobby_count_by_student=Count(’hobbies‘))

# 按愛好分組,再統計每組學生數量。

Hobby。objects。annotate(Count(’student‘))

# 按愛好分組,再統計每組學生最大年齡。

Hobby。objects。annotate(Max(’student__age‘))

Annotate方法與Filter方法聯用

有時我們需要先對資料集先篩選再分組,有時我們還需要先分組再對查詢集進行篩選。根據需求不同,我們可以合理地聯用annotate方法和filter方法。注意: annotate和filter方法聯用時使用順序很重要。

# 先按愛好分組,再統計每組學生數量, 然後篩選出學生數量大於1的愛好。

Hobby。objects。annotate(student_num=Count(’student‘))。filter(student_num__gt=1)

# 先按愛好分組,篩選出以’d‘開頭的愛好,再統計每組學生數量。

Hobby。objects。filter(name__startswith=“d”)。annotate(student_num=Count(’student‘))

Annotate與order_by()聯用

我們同樣可以使用order_by方法對annotate方法返回的資料集進行排序。

# 先按愛好分組,再統計每組學生數量, 然後按每組學生數量大小對愛好排序。

Hobby。objects。annotate(student_num=Count(‘student’))。order_by(‘student_num’)

# 統計最受學生歡迎的5個愛好。

Hobby。objects。annotate(student_num=Count(‘student’))。order_by(‘-student_num’)[:5]

Annotate與values()聯用

我們在前例中按學生物件進行分組,我們同樣可以按學生姓名name來進行分組。唯一區別是本例中,如果兩個學生具有相同名字,那麼他們的愛好數量將疊加。

# 按學生名字分組,統計每個學生的愛好數量。

Student。objects。values(‘name’)。annotate(Count(‘hobbies’))

你還可以使用values方法從annotate返回的資料集裡提取你所需要的欄位,如下所示:

# 按學生名字分組,統計每個學生的愛好數量。

Student。objects。annotate(hobby_count=Count(‘hobbies’))。values(‘name’, ‘hobby_count’)

小結

Django的aggregate和annotate方法屬於高階查詢方法,主要用於組合查詢,可以大大提升資料庫查詢效率。當你需要對查詢集(queryset)的某些欄位進行聚合操作時(比如Sum, Avg, Max),請使用aggregate方法。如果你想要對資料集先進行分組(Group By)然後再進行某些聚合操作或排序時,請使用annotate方法。最後希望本文提供的一些示例對你有所幫助哦。

大江狗

2018。11。27

http://

weixin。qq。com/r/F3STl03

E14WrKQn0byGT

(二維碼自動識別)

標簽: annotate  AGE  aggregate  student  objects