Django: aggregate和annotate方法使用详解与示例

本文介绍了Django的高级查询方法aggregate和annotate。当需对查询集字段计算或分组计算、排序时,可使用这两个方法。aggregate用于对一组值统计计算并返回字典,annotate用于先分组再聚合操作或排序,返回含新增统计字段的查询集,还介绍了它们与其他方法联用的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前面的文章和案例里,我们从数据库里查询数据一般只使用了一些初级的查询方法比如filter()和exclude()方法。但如果查询本身比较复杂,比如需要对查询集(queryset)的某些字段进行计算或进行分组计算或排序, 这时我们就需要使用更高级的aggregate和annotate方法了。小编我今天就带你看下什么情况下需要使用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方法。最后希望本文提供的一些示例对你有所帮助哦。

### Django ORM QuerySet 方法汇总及其功能详解 Django 的 ORM 提供了一套强大的工具来简化数据库的操作,其中 QuerySet 是核心概念之一。下面列举了常见的 QuerySet 方法,并对其功能进行了详细介绍。 --- #### 1. **查询方法** ##### (1) `filter(**kwargs)` - **功能**: 返回一个新的 QuerySet,包含满足给定条件的所有对象。 - **惰性执行**: 不会立即执行查询,直到实际使用数据时才会触发 SQL 请求。 - **示例**: ```python users = User.objects.filter(is_active=True) ``` [^1] ##### (2) `exclude(**kwargs)` - **功能**: 返回排除指定条件的对象集合。 - **特点**: `filter` 相反,用于否定条件的筛选。 - **示例**: ```python inactive_users = User.objects.exclude(is_active=True) ``` ##### (3) `get(**kwargs)` - **功能**: 获取唯一匹配的对象。如果找到多个或找不到任何对象,则抛出异常。 - **注意**: 如果没有找到结果,会引发 `DoesNotExist`;如果有多个结果,会引发 `MultipleObjectsReturned`。 - **示例**: ```python user = User.objects.get(username='admin') ``` ##### (4) `first() / last()` - **功能**: 返回 QuerySet 中的第一个/最后一个对象。如果没有对象则返回 `None`。 - **示例**: ```python first_user = User.objects.first() last_user = User.objects.last() ``` --- #### 2. **聚合统计方法** ##### (1) `count()` - **功能**: 计算 QuerySet 中的对象数量。 - **示例**: ```python total_users = User.objects.count() ``` ##### (2) `aggregate(*args, **kwargs)` - **功能**: 执行聚合计算(如求、平均值等),返回字典形式的结果。 - **依赖模块**: 需要导入 `django.db.models` 下的相关函数。 - **示例**: ```python from django.db.models import Avg, Sum average_age = User.objects.aggregate(avg_age=Avg('age')) sum_salary = Employee.objects.aggregate(total_salary=Sum('salary')) ``` ##### (3) `exists()` - **功能**: 检查是否存在至少一个对象,通常用于优化布尔判断。 - **优点**: 性能优于 `bool(queryset)`。 - **示例**: ```python if User.objects.exists(): print("There are users.") ``` --- #### 3. **修改更新方法** ##### (1) `update(field=value, ...)` - **功能**: 更新 QuerySet 中所有对象的一个或多个字段。 - **注意事项**: 此操作不会调用模型的 `save()` 方法,因此信号监听器不会生效。 - **示例**: ```python User.objects.filter(is_active=False).update(last_login=timezone.now()) ``` ##### (2) `create(**kwargs)` - **功能**: 创建并保存新的对象到数据库中。 - **示例**: ```python new_user = User.objects.create(username='test', email='test@example.com') ``` ##### (3) `bulk_create(objs, batch_size=None)` - **功能**: 批量创建多个对象,性能更高。 - **示例**: ```python objs = [User(username=f"user{i}") for i in range(10)] User.objects.bulk_create(objs) ``` ##### (4) `delete()` - **功能**: 删除 QuerySet 中的所有对象。 - **示例**: ```python User.objects.filter(is_active=False).delete() ``` --- #### 4. **序列化转换方法** ##### (1) `values(*fields)` - **功能**: 返回只包含指定字段的字典列表。 - **示例**: ```python user_data = User.objects.values('username', 'email') ``` [^3] ##### (2) `values_list(*fields, flat=False)` - **功能**: 返回元组列表,当设置 `flat=True` 且只有一个字段时,返回单一值列表。 - **示例**: ```python emails = User.objects.values_list('email', flat=True) ``` ##### (3) `dates(field, kind, order='ASC')` - **功能**: 返回按日期分组的独特值。 - **示例**: ```python unique_dates = Entry.objects.dates('pub_date', 'day') ``` --- #### 5. **排序切片方法** ##### (1) `order_by(*fields)` - **功能**: 根据指定字段对结果进行排序,默认升序,降序加 `-` 前缀。 - **示例**: ```python sorted_users = User.objects.order_by('-date_joined') ``` ##### (2) `reverse()` - **功能**: 反转当前 QuerySet 的顺序。 - **示例**: ```python reversed_users = User.objects.all().reverse() ``` ##### (3) `extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)` - **功能**: 支持自定义 SQL 查询片段。 - **示例**: ```python custom_ordering = Blog.objects.extra(order_by=['-rating']) ``` [^4] --- #### 6. **缓存延迟加载** ##### (1) `iterator(chunk_size=2000)` - **功能**: 逐批迭代大 QuerySet,节省内存占用。 - **示例**: ```python for user in User.objects.iterator(): process(user) ``` ##### (2) `defer(*fields)` - **功能**: 推迟某些字段的加载,减少初始查询的数据量。 - **示例**: ```python lazy_load = Entry.objects.defer("body") ``` ##### (3) `only(*fields)` - **功能**: 仅加载指定字段,其余字段在首次访问时再加载。 - **示例**: ```python optimized_load = Entry.objects.only("title", "author") ``` --- #### 7. **其他实用方法** ##### (1) `distinct(*fields)` - **功能**: 移除重复项,支持基于特定字段去重。 - **示例**: ```python unique_authors = Entry.objects.distinct('author') ``` ##### (2) `annotate(*args, **kwargs)` - **功能**: 为每个对象附加额外的信息(如计数或其他聚合值)。 - **示例**: ```python from django.db.models import Count authors_with_post_count = Author.objects.annotate(post_count=Count('posts')) ``` ##### (3) `prefetch_related(*lookups)` - **功能**: 减少 N+1 查询问题,预先获取相关联的数据。 - **示例**: ```python books_with_authors = Book.objects.prefetch_related('authors') ``` ##### (4) `select_related(*fields)` - **功能**: 对于一对一或多对一关系,提前加载关联表数据。 - **示例**: ```python entries_with_blog = Entry.objects.select_related('blog') ``` --- ### 结论 以上总结了 Django ORM QuerySet 的主要方法及其应用场景。每种方法都有其独特的用途,在开发过程中应根据实际情况灵活选用,以达到最佳性能可维护性的平衡。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值