annotate和aggregate的使用场景

假如我们有如下的模型,其中PeopleHobby为多对多的关系,我们想知道people的平均年龄,我们常规的做法是利用for循环查出符合条件的people,再把他们的年龄取出来相加,然后再除以总人数。当人数非常多而我们又只需要平均年龄的时候,这样的方法是很浪费资源的,一个更好的方法就是在数据库层面就直接返回我们所需的信息,此时的aggregate方法就可以帮我们大大提升效率。

另一个例子是统计最受人们欢迎的5个爱好,常规做法是先将所有hobby对象提取出来,载入内存。然后利用for循环统计每组爱好对应的人数,再构建一个新的查询集,按每组人数从大到小进行排序。这个查询需要根据hobby先进行分组,再统计每个爱好组里人的数量,然后进行排序。对于这个复杂查询, annotate方法一句话就可以解决问题。

class Hobby(models.Model):

    name = models.CharField(max_length=20)


class People(models.Model):

    name = models.CharField(max_length=20)
    age = models.IntegerField(default=0)
    hobbies = models.ManyToManyField(Hobby)    

aggregate方法详解

aggregate的中文意思是聚合, 源于SQL的聚合函数。Djangoaggregate()方法作用是对一组值(比如queryset的某个字段)进行统计计算,并以字典(Dict)格式返回统计计算结果。djangoaggregate方法支持的聚合操作有AVG/COUNT/MAX/MIN/SUM 等

我们现在来看下几组实际使用案例。使用前别忘了导入Avg, Max, Min或者Sum方法

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

# 计算people平均年龄, 返回字典。age和avg间是双下划线
People.objects.aggregate(Avg('age'))

# 同时获取people年龄均值, 最大值和最小值, 返回字典
People.objects.aggregate(Avg('age'), Max('age'), Min('age'))

# 根据Hobby反查People最大年龄
Hobby.objects.aggregate(Max('people__age'))

annotate方法详解

annotateDjango就是分组的意思,类似于SQL中的group by,如果想要对数据先进性分组然后再进行某些聚合操作或排序时,就可以使用annotate方法来实现,annotate返回的是包含新增统计字段的queryset

接下来我们来看几个实例:

# 按people分组,统计每个people的爱好数量
People.objects.annotate(Count("bobbies"))

返回的结果依然是People的查询集,只不过多了hobbies_count这个字段,也可以自定义字段名

# 按people分组,统计每个people的爱好数量,并自定义字段名
People.objects.annotate(people_hobby_count=Count("bobbies"))

我们在使用annotate时也可以和filter一起使用

# 按people分组,统计每个people的爱好(爱好为看书)数量
People.objects.annotate(Count("bobbies", filter=Q(name="看书")))

版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:https://www.ltfred.com/article/django-annotate-aggregate/