Django Aggregation聚合使用方法解析


Posted in Python onAugust 01, 2019

在当今根据需求而不断调整而成的应用程序中,通常不仅需要能依常规的字段,如字母顺序或创建日期,来对项目进行排序,还需要按其他某种动态数据对项目进行排序。Djngo聚合就能满足这些要求。

以下面的Model为例

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)
  num_awards = models.IntegerField()
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)
  pubdate = models.DateField() 
class Store(models.Model):
  name = models.CharField(max_length=300)
  books = models.ManyToManyField(Book)
  registered_users = models.PositiveIntegerField()

快速了解

# books总数量.
>>> Book.objects.count()
2452
 
# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73
 
# books的平均price.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
 
# books的最大price.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}
 
# All the following queries involve traversing the Book<->Publisher
# many-to-many relationship backward
 
# 为每个publisher添加个num_books属性,即每个pulisher出版的book的数量.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
[<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]
>>> pubs[0].num_books
73
 
# 根据num_book属性排序.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323

聚合生成Generating aggregates over a QuerySet

Django有两种方法来生成聚合。第一种方法是为整个QuerySet生成聚合值,例如为全部的books生成price的平均值:

>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

可以简略为:

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

函数aggregate()的参数是一系列聚合函数aggregate functions:

Avg

返回平均值

Count

class Count(field, distinct=False)

返回计数。当参数distinct=True时,返回unique的对象数目。

Max

返回最大值

Min

返回最小值.

StdDev

class StdDev(field, sample=False)

返回标准偏差

有一个参数sample

默认情况下sample=False,返回总体标准偏差,如果sample=True,返回样本标准偏差。

Sum

返回总值

Variance

class Variance(field, sample=False)

返回方差

有一个参数sample,默认返回总体方差,sample设为True时返回样本方差。

aggregate()方法被调用时,返回一个键值对字典,可以指定key的名字:

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

如果你想生成多个聚合,你只需要添加另一个参数。所以,如果我们还想知道所有书的最高和最低的价格:

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

为查询集的每个对象生成聚合值Generating aggregates for each item in a QuerySet

这是生成聚合值的第二种方法。比如你要检索每本书有多少个作者。book和author是manytomany的关系,我们可以为每本书总结出这种关系。

每个对象的总结可以用方法annotate()生成:

# 建立一个annotate QuerySet
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors'))
# 第一个对象
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# 第二个对象
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

也可以指定生成属性的名字:

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

和aggregate()不同,annotate()的输出是一个QuerySet。

联合聚合Joins and aggregates

目前为止,我们聚合查询的field都属于我们要查询的Model,我们也可以用其它Model的field来进行聚合查询,例如:

>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

这样就可以查询每个Store里面books的价格范围

联合链的深度可以随心所欲:

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

反向关系Following relationships backwards

通过book反向查询publisher:

>>> from django.db.models import Count, Min, Sum, Avg
>>> Publisher.objects.annotate(Count('book'))

返回的QuerySet的每个publisher都会带一个属性book_count。

查询出版最久的书的出版日期:

>>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))

查询每个作者写的书的总页数:

>>> Author.objects.annotate(total_pages=Sum('book__pages'))

查询所有作者写的书的平均rating:

>>> Author.objects.aggregate(average_rating=Avg('book__rating'))

聚合和其它查询集操作Aggregations and other QuerySet clauses

filter() and exclude()

聚合可以和filter和exclude一起使用:

>>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

可以根据聚合值进行筛选:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

编写一个包含annotate()和filter()从句的复杂查询时,要特别注意作用于QuerySet的从句的顺序顺序的不同,产生的意义也不同:

>>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)
>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

两个查询都返回了至少出版了一本好书(评分大于3分)的出版商的列表。但是第一个查询的注解包含其该出版商发行的所有图书的总数;而第二个查询的注解只包含出版过好书的出版商的所发行的好书(评分大于3分)总数。在第一个查询中,注解在过滤器之前,所以过滤器对注解没有影响。

在第二个查询中,过滤器在注解之前,所以,在计算注解值时,过滤器就限制了参与运算的对象的范围

order_by()

可以根据聚合值进行排序:

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

values()

通常,注解annotate是添加到每一个对象上的,一个执行了注解操作的查询集 QuerySet 所返回的结果中,每个对象都添加了一个注解值。但是,如果使用了values()从句,它就会限制结果中列的范围,对注解赋值的方法就会完全不同。就不是在原始的 QuerySet 返回结果中对每个对象中添加注解,而是根据定义在 values() 从句中的字段组合对先结果进行唯一的分组,再根据每个分组算出注解值,这个注解值是根据分组中所有的成员计算而得的:

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

这样的写法下,QuerySet会根据name进行组合,返回的是每个unique name的聚合值。如果有两个作者有相同的名字,这两个作者会被当做一个计算,他们的books会合在一起。

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

位置互换后,会为每个author都生成一个average_rating,而且只会输出每个author的name和average_rating。

默认排序下使用聚合:

from django.db import models
 
class Item(models.Model):
  name = models.CharField(max_length=10)
  data = models.IntegerField()
 
  class Meta:
    ordering = ["name"]

如果你想知道每个非重复的data值出现的次数,你可能这样写:

# Warning: 不正确的写法
Item.objects.values("data").annotate(Count("id"))

这部分代码想通过使用它们公共的data值来分组Item对象,然后在每个分组中得到id值的总数。但是上面那样做是行不通的。这是因为默认排序项中的name也是一个分组项,所以这个查询会根据非重复的(data,name)进行分组,而这并不是你本来想要的结果。所以,你需要这样写来去除默认排序的影响:

Item.objects.values("data").annotate(Count("id")).order_by()

Aggregating annotations

也可以根据annotation结果生成聚合值,例如计算每本书平均有几个作者:

>>> from django.db.models import Count, Avg
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
pyramid配置session的方法教程
Nov 27 Python
Python基于OpenCV库Adaboost实现人脸识别功能详解
Aug 25 Python
Scrapy-Redis结合POST请求获取数据的方法示例
May 07 Python
python+openCV利用摄像头实现人员活动检测
Jun 22 Python
django项目环境搭建及在虚拟机本地创建django项目的教程
Aug 02 Python
python爬虫 2019中国好声音评论爬取过程解析
Aug 26 Python
python使用opencv实现马赛克效果示例
Sep 28 Python
Python 剪绳子的多种思路实现(动态规划和贪心)
Feb 24 Python
解决Django中checkbox复选框的传值问题
Mar 31 Python
PyQt5 文本输入框自动补全QLineEdit的实现示例
May 13 Python
通过实例了解python__slots__使用方法
Sep 14 Python
pycharm 的Structure界面设置操作
Feb 05 Python
Flask教程之重定向与错误处理实例分析
Aug 01 #Python
python gdal安装与简单使用
Aug 01 #Python
Django模型修改及数据迁移实现解析
Aug 01 #Python
Django 大文件下载实现过程解析
Aug 01 #Python
python爬虫刷访问量 2019 7月
Aug 01 #Python
用Cython加速Python到“起飞”(推荐)
Aug 01 #Python
Python爬取视频(其实是一篇福利)过程解析
Aug 01 #Python
You might like
PHP新手NOTICE错误常见解决方法
2011/12/07 PHP
php返回当前日期或者指定日期是周几
2015/05/21 PHP
windows8.1下Apache+Php+MySQL配置步骤
2015/10/30 PHP
Laravel 关联模型-关联新增和关联更新的方法
2019/10/10 PHP
[原创]网络复制内容时常用的正则+editplus
2006/11/30 Javascript
extjs form textfield的隐藏方法
2008/12/29 Javascript
javaScript同意等待代码实现心得
2011/01/01 Javascript
javascript记住用户名和登录密码(两种方式)
2015/08/04 Javascript
jquery+css实现绚丽的横向二级下拉菜单-附源码下载
2015/08/23 Javascript
JavaScript实现图片瀑布流和底部刷新
2017/01/02 Javascript
Node.js中的http请求客户端示例(request client)
2017/05/04 Javascript
JavaScript限制在客户区可见范围的拖拽(解决scrollLeft和scrollTop的问题)(2)
2017/05/17 Javascript
javascript兼容性(实例讲解)
2017/08/15 Javascript
vue移动端屏幕适配详解
2019/04/30 Javascript
Vue 实现复制功能,不需要任何结构内容直接复制方式
2019/11/09 Javascript
[01:27]DOTA2电竞之夜 今夜共饮庆功酒
2014/08/02 DOTA
Python实现的检测web服务器健康状况的小程序
2014/09/17 Python
RC4文件加密的python实现方法
2015/06/30 Python
pygame实现弹力球及其变速效果
2017/07/03 Python
详解Python的数据库操作(pymysql)
2019/04/04 Python
python实现对象列表根据某个属性排序的方法详解
2019/06/11 Python
python2爬取百度贴吧指定关键字和图片代码实例
2019/08/14 Python
python输入错误后删除的方法
2019/10/12 Python
OpenCV哈里斯(Harris)角点检测的实现
2020/01/15 Python
python中的unittest框架实例详解
2021/02/05 Python
处理HTML5新标签的浏览器兼容版问题
2017/03/13 HTML / CSS
学校消防安全制度
2014/01/30 职场文书
毕业生如何写自荐信
2014/03/26 职场文书
学校联谊协议书
2014/09/16 职场文书
2014教师党员自我评议(5篇)
2014/09/20 职场文书
具结保证书
2015/01/17 职场文书
党员反腐倡廉学习心得体会
2015/08/15 职场文书
升学宴学生致辞
2015/09/29 职场文书
感恩信:写给爸爸妈妈的一封感谢信
2019/09/12 职场文书
java解析XML详解
2021/07/09 Java/Android
Python pyecharts案例超市4年数据可视化分析
2022/08/14 Python