在前面的文章和案例里,我们从数据库里查询数据一般只使用了一些初级的查询方法比如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方法。最后希望本文提供的一些示例对你有所帮助哦。
大江狗
2018.11.27
Django Web开发核心基础知识
Django网站开发四件套是如何遵循MVC软件设计模式的?
Django基础核心技术介绍(1): Model模型的介绍与设计
Django基础核心技术介绍(2): URL的设计与配置
Django基础核心技术介绍(3): View视图详解与通用视图
Django基础核心技术介绍(4): Template模板的编写及过滤器
Django基础核心结束介绍(5): Forms表单的使用与设计
Django基础(6): 模型Models高级进阶必读。
Django基础(7): cookie和session应用场景及如何使用
Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用
Django基础(9): 表单Forms的高级使用技巧
Django基础(10): URL重定向的HttpResponseDirect, redirect和reverse的用法
Django基础(11): 表单集合Formset的高级用法
Django基础(12): Request对象详解及开发显示用户IP地址和浏览器APP
Django基础(13): 深夜放干货。QuerySet特性及高级使用技巧,如何减少数据库的访问,节省内存,提升网站性能。
Django基础(14): 通过next参数实现登录后跳转回到前一页的3种方法
Django基础(15): 模板过滤器(filter)的工作原理及如何自定义模板过滤器
Django基础(16): 模板标签(tags)的分类及如何自定义模板标签
Django基础(17): 如何上传处理文件及Ajax文件上传示范(附GitHub源码)
Django基础(18): 实现文件下载的3种方法及文件私有化
Django基础(19): Django Admin管理后台详解(上)
Django基础(20): Django admin管理后台详解(中)如何自定义list_display和list_filter
Django基础(21): Django admin管理后台详解(下)如何自定义actions, 表单和美化admin
Django基础(22): 数据库的设计之自定义表名,建立索引和使用多数据库主从配置
Django基础(23): 权限管理(permissions)与用户组(group)详解
Django基础(24): aggregate和annotate方法使用详解与示例
Django基础(25):settings.py设置选项深入解读。大江狗精品原创。
Django基础(26): 常用装饰器应用场景及正确使用方法
Django基础(27): 快捷函数(shortcut function)模块详解Django基础(28): 如何设计充满陷阱的优美URL
Django基础(28): 如何设计充满陷阱的优美URL
Django Web开发实战案例
Django 2.0 项目实战(1): 扩展Django自带User模型,实现用户注册与登录
Django 2.0 项目实战(2): 编辑用户个人资料,扩展Django后台UserAdmin
Django 2.0项目实战(3): 密码重置与退出登录
Django 2.0 项目实战: 图片上传与显示
Django 2.0 项目实战: PDF文件页面提取
Django 2.0 项目实战: PDF文件合并
Django 2.0 项目实战:输出树形分类目录
Django 2.0 项目实战: 网页计数器统计浏览次数
Django 2.0 项目实战: 利用AJAX实现博文实时搜索
Django 1.X和2.0下利用自带分页Paginator类实现分页功能
Django实战: 利用Ajax生成联动下拉菜单
世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(1)
世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(2)
django-allauth教程(1): 安装,用户注册,登录,邮箱验证和密码重置(更新)
django-allauth教程(2): 用户个人资料UserProfile扩展与编辑
django-allauth教程(3): 第三方账户授权登录(以百度账号为例)
django-allauth教程(4): 美化模板,自定义邮件和消息内容
Django+jQuery cropper实现用户头像裁剪, 预览和上传[原创]
Django实战教程: 开发餐厅在线点评网站(1)
Django实战教程: 开发餐厅在线点评网站(2)
Django实战教程: 开发企业级应用智能文档管理系统smartdoc(1)
Django实战教程: 开发企业级应用智能文档管理系统smartdoc(2)之权限管理
Django实战教程: 开发企业级应用智能文档管理系统smartdoc(3)附GitHub代码地址
Django实战专题: 开发专业博客(1)之内容管理后台开发
Django实战专题: 开发专业博客(2)之母子类别导航和添加富文本编辑器CKEditor
Django实战专题: 开发专业博客(3)之仿微信评论点赞功能
Django实战: Python爬取链家上海二手房信息,存入数据库并在前端显示
Django应用实战: 编写你自己的PDF编辑器, 实现PDF页面提取, 页面合并与替换。
如何在阿里云Ubuntu服务器通过uWSGI和Nginx部署Django项目教程-大江狗原创出品
Django Web开发学习笔记
浅谈Django Model创建对象的save与create方法
Django模板设置全局变量(默认变量)
Django常用命令django-admin.py和manage.py用法详解
Django自定义图片和文件上传路径(upload_to)的2种方式
Django ContentTypes框架详解及使用场景介绍
Django更改模型过程中易出现的问题及解决方案
2019新年第一篇: SQLite的优缺点及Django配置MySQL数据库