Django的aggregate和annotate方法屬於高級查詢方法,主要用於組合查詢,是Django高手們必須要熟練掌握的。當咱們須要對查詢集(queryset)的某些字段進行計算或進行先分組再計算或排序, 咱們就須要使用aggregate和annotate方法了。數據庫
假如咱們有以下一個模型,其中Student與Hobby(愛好)是多對多的關係。咱們想要知道全部學生的平均年齡,咱們常規作法通常是利用for循環從數據庫中把符合查詢條件的student對象一個一個取出,把他們年齡相加,而後再除以總人數。當人數很是多而咱們又只須要平均年齡這條信息時,把全部符合查詢條件的學生對象都載入內存後再進行計算是很是浪費資源的,效率也很是低。一個更好的方法是在數據庫層面提取查詢數據時就直接返回咱們所須要的信息。由於這個查詢涉及到對整個queryset的age字段進行統計計算,此時django的聚合函數方法aggregate就能夠幫咱們大大提高查詢效率了[見後文]。django
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的中文意思是聚合, 源於SQL的聚合函數。Django的aggregate()方法做用是對一組值(好比queryset的某個字段)進行統計計算,並以字典(Dict)格式返回統計計算結果。django的aggregate方法支持的聚合操做有AVG / COUNT / MAX / MIN /SUM 等。spa
咱們如今來看下幾組實際使用案例。使用前別忘了import Avg, Max, Min或者Sum方法哦code
from django.db.models import Avg, Max, Min
# 計算學平生均年齡, 返回字典。age和avg間是雙下劃線哦對象
Student.objects.all().aggregate(Avg('age')) { 'age__avg': 12 }
# 學平生均年齡,返回字典。all()不是必須的。blog
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的中文意思是註釋,小編我以爲是很是地詞不達意,一個更好的理解是分組(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方法聯用時使用順序很重要。
# 先按愛好分組,再統計每組學生數量, 而後篩選出學生數量大於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‘))
咱們一樣可使用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]
咱們在前例中按學生對象進行分組,咱們一樣能夠按學生姓名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方法。