aggregate() 是 QuerySet 的一个终端子句,使用后将返回“名称-值”的字典
annotate() 不是终端子句。annotate() 子句的输出就是 QuerySet
与aggregate方法不同的是,annotate方法返回结果的不仅仅是含有统计结果的一个字典,而是包含有新增统计字段的查询集(queryset)
假如Student和Hobby是多对多
# 按学生分组,统计每个学生的爱好数量
Student.objects.annotate(Count('hobbies'))
# 按爱好分组,统计每个爱好的学生数量。
Hobby.objects.annotate(Count('student'))
又例如:
annotate() 和 filter() 子句的顺序
当开发一个涉及 annotate() 和 filter() 子句的复杂查询时,要特别注意应用于 QuerySet 的子句的顺序。
当一个 annotate() 子句应用于查询,会根据查询状态来计算注解,直到请求的注解为止。这实际上意味着 filter() 和 annotate() 不是可交换的操作。
比如:
出版者A有两本评分4和5的书。
出版者B有两本评分1和4的书。
出版者C有一本评分1的书。
下面就是 Count 聚合的例子:
>>> a, b = Publisher.objects.annotate(num_books=Count('book', distinct=True)).filter(book__rating__gt=3.0)
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 2)
>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 1)
两个查询返回出版者列表,这些出版者至少有一本评分3的书,因此排除了C。
在第一个查询里,注解优先于过滤器,因此过滤器没有影响注解。distinct=True 用来避免 a query bug。
第二个查询每个发布者评分3以上的书籍数量。过滤器优先于注解,因此过滤器约束计算注解时考虑的对象。
这里是另一个关于 Avg 聚合的例子:
>>> a, b = Publisher.objects.annotate(avg_rating=Avg('book__rating')).filter(book__rating__gt=3.0)
>>> a, a.avg_rating
(<Publisher: A>, 4.5) # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 2.5) # (1+4)/2
>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(avg_rating=Avg('book__rating'))
>>> a, a.avg_rating
(<Publisher: A>, 4.5) # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 4.0) # 4/1 (book with rating 1 excluded)
第一个查询请求至少有一本评分3以上的书籍的出版者的书籍平均分。第二个查询只请求评分3以上的作者书籍的平均评分。