Django中Aggregation聚合的基本使用方法


Posted in Python onJuly 09, 2020

Django 的 filter、exclude 等方法使得对数据库的查询很方便了。这在数据量较小的时候还不错,但如果数据量很大,或者查询条件比较复杂,那么查询效率就会很低。

提高数据库查询效率可以通过原生 SQL 语句来实现,但是它的缺点就是需要开发者熟练掌握 SQL。倘若查询条件是动态变化的,则编写 SQL 会更加困难。

对于以便捷著称的 Django,怎么能忍受这样的事。于是就有了 Aggregation聚合 。

聚合最好的例子就是官网给的案例了:

# models.py

from django.db import models

class Author(models.Model):
  name = models.CharField(max_length=100)
  age = models.IntegerField()

class Publisher(models.Model):
  name = models.CharField(max_length=300)

class Book(models.Model):
  name = models.CharField(max_length=300)
  pages = models.IntegerField()
  price = models.DecimalField(max_digits=10, decimal_places=2)
  rating = models.FloatField()
  authors = models.ManyToManyField(Author)
  publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
  pubdate = models.DateField()

class Store(models.Model):
  name = models.CharField(max_length=300)
  books = models.ManyToManyField(Book)

接下来可以这样求所有书籍的平均价格:

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

>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': Decimal('30.67')}

实际上可以省掉 all() :

>>> Book.objects.aggregate(Avg('price'))
{'price__avg': Decimal('30.67')}

还可以指定返回的键名:

>>> Book.objects.aggregate(price_avg=Avg('price'))
{'price_avg': Decimal('30.67')}

如果要获取所有书籍中的最高价格:

>>> Book.objects.aggregate(Max('price'))
{'price__max': Decimal('44')}

获取所有书籍中的最低价格:

>>> Book.objects.aggregate(Min('price'))
{'price__min': Decimal('12')}

aggregate() 方法返回的不再是 QuerySet 了,而是一个包含查询结果的字典。如果我要对 QerySet 中每个元素都进行聚合计算、并且返回的仍然是 QuerySet ,那就要用到 annotate() 方法了。

annotate 翻译过来就是 注解 ,它的作用有点像给 QuerySet 中的每个元素临时贴上一个临时的字段,字段的值是分组聚合运算的结果。

比方说要给查询集中的每本书籍都增加一个字段,字段内容是外链到书籍的作者的数量:

>>> from django.db.models import Count

>>> q = Book.objects.annotate(Count('authors'))
>>> q[0].authors__count
3

与 aggregate() 的语法类似,也可以给这个字段自定义个名字:

>>> q = Book.objects.annotate(a_count=Count('authors'))

跨外链查询字段也是可以的:

>>> s = Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

>>> s[0].min_price
Decimal('12')
>>> s[0].max_price
Decimal('44')

既然 annotate() 返回的是查询集,那么自然也可以和 filter() 、 exclude() 等查询方法组合使用:

>>> b = Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
>>> b[0].num_authors
4

联用的时候 filter 、 annotate 的顺序会影响返回结果,所以逻辑要想清楚。

也可以排序:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

总而言之, aggregate 和 annotate 用于组合查询。当你需要对某些字段进行聚合操作时(比如Sum, Avg, Max),请使用 aggregate 。如果你想要对数据集先进行分组(Group By)然后再进行某些聚合操作或排序时,请使用 annotate 。

进行此类查询有时候容易让人迷惑,如果你对查询的结果有任何的疑问,最好的方法就是直接查看它所执行的 SQL 原始语句,像这样:

>>> b = Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
>>> print(b.query)
SELECT "aggregation_book"."id", "aggregation_book"."name",
"aggregation_book"."pages", "aggregation_book"."price",
"aggregation_book"."rating", "aggregation_book"."publisher_id", 
"aggregation_book"."pubdate", COUNT("aggregation_book_authors"."author_id") 
AS "num_authors" FROM "aggregation_book" LEFT OUTER JOIN "aggregation_book_authors" 
ON ("aggregation_book"."id" = "aggregation_book_authors"."book_id") 
GROUP BY "aggregation_book"."id", "aggregation_book"."name",
"aggregation_book"."pages", "aggregation_book"."price",
"aggregation_book"."rating", "aggregation_book"."publisher_id", 
"aggregation_book"."pubdate"
ORDER BY "num_authors" ASC

相关文档: Aggregation

复合使用聚合时的相互干扰问题: Count and Sum annotations interfere with each other

总结

到此这篇关于Django中Aggregation聚合的基本使用方法就介绍到这了,更多相关Django Aggregation聚合使用内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python的另外几种语言实现
Jan 29 Python
Python本地与全局命名空间用法实例
Jun 16 Python
python发送邮件功能实现代码
Jul 15 Python
python实现根据指定字符截取对应的行的内容方法
Oct 23 Python
解决Pandas的DataFrame输出截断和省略的问题
Feb 08 Python
opencv resize图片为正方形尺寸的实现方法
Dec 26 Python
python列表返回重复数据的下标
Feb 10 Python
浅谈python元素如何去重,去重后如何保持原来元素的顺序不变
Feb 28 Python
Jupyter 无法下载文件夹如何实现曲线救国
Apr 22 Python
Python定义一个函数的方法
Jun 15 Python
把Anaconda中的环境导入到Pycharm里面的方法步骤
Oct 30 Python
Django contrib auth authenticate函数源码解析
Nov 12 Python
Python  word实现读取及导出代码解析
Jul 09 #Python
推荐技术人员一款Python开源库(造数据神器)
Jul 08 #Python
实例讲解Python 迭代器与生成器
Jul 08 #Python
opencv 阈值分割的具体使用
Jul 08 #Python
如何表示python中的相对路径
Jul 08 #Python
如何卸载python插件
Jul 08 #Python
python中数字是否为可变类型
Jul 08 #Python
You might like
一些星际专用术语解释
2020/03/04 星际争霸
PHP VS ASP
2006/10/09 PHP
网页游戏开发入门教程三(简单程序应用)
2009/11/02 PHP
ajax 的post方法实例(带循环)
2011/07/04 PHP
用穿越火线快速入门php面向对象
2012/02/22 PHP
PHP--用万网的接口实现域名查询功能
2012/12/13 PHP
laravel5.4利用163邮箱发送邮件的步骤详解
2017/09/22 PHP
js中if语句的几种优化代码写法
2011/03/12 Javascript
js通过googleAIP翻译PHP系统的语言配置的实现代码
2011/10/17 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
2013/08/09 Javascript
jQuery Trim去除字符串首尾空字符的实现方法说明
2014/02/11 Javascript
js实现随屏幕滚动的带缓冲效果的右下角广告代码
2015/09/04 Javascript
Node.js中的http请求客户端示例(request client)
2017/05/04 Javascript
JavaScript函数表达式详解及实例
2017/05/05 Javascript
Angular 表单控件示例代码
2017/06/26 Javascript
用JS实现简单的登录验证功能
2017/07/28 Javascript
vue解决一个方法同时发送多个请求的问题
2018/09/25 Javascript
JavaScript 常见的继承方式汇总
2020/09/17 Javascript
[28:28]Ti4 冒泡赛第二天NEWBEE vs NaVi 2
2014/07/15 DOTA
Python函数的参数常见分类与用法实例详解
2019/03/30 Python
Django框架中序列化和反序列化的例子
2019/08/06 Python
Django框架model模型对象验证实现方法分析
2019/10/02 Python
Python tkinter布局与按钮间距设置方式
2020/03/04 Python
pyqt5 QlistView列表显示的实现示例
2020/03/24 Python
python 6.7 编写printTable()函数表格打印(完整代码)
2020/03/25 Python
python使用opencv resize图像不进行插值的操作
2020/07/05 Python
英国、欧洲和全球租车服务:Avis英国
2016/08/29 全球购物
高中生学习的自我评价
2013/12/14 职场文书
经典安踏广告词
2014/03/21 职场文书
政风行风整改方案
2014/10/25 职场文书
科技馆观后感
2015/06/08 职场文书
通讯稿格式及范文
2015/07/22 职场文书
关于社会实践的心得体会(2016最新版)
2016/01/25 职场文书
教你怎么用Python实现多路径迷宫
2021/04/29 Python
简单介绍Python的第三方库yaml
2021/06/18 Python
深入理解MySQL中MVCC与BufferPool缓存机制
2022/05/25 MySQL