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 相关文章推荐
Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
Sep 06 Python
python使用cStringIO实现临时内存文件访问的方法
Mar 26 Python
在RedHat系Linux上部署Python的Celery框架的教程
Apr 07 Python
Python用zip函数同时遍历多个迭代器示例详解
Nov 14 Python
Pandas之drop_duplicates:去除重复项方法
Apr 18 Python
使用python装饰器计算函数运行时间的实例
Apr 21 Python
Python函数返回不定数量的值方法
Jan 22 Python
Python静态类型检查新工具之pyright 使用指南
Apr 26 Python
python字典改变value值方法总结
Jun 21 Python
Python自动采集微信联系人的实现示例
Feb 28 Python
python操作微信自动发消息的实现(微信聊天机器人)
Jul 14 Python
python3 中时间戳、时间、日期的转换和加减操作
Jul 14 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 创建文件(文件夹)以及目录操作代码
2010/03/04 PHP
PHP程序员最常犯的11个MySQL错误小结
2010/11/20 PHP
LotusPhp笔记之:Cookie组件的使用详解
2013/05/06 PHP
PHP版本常用的排序算法汇总
2015/12/20 PHP
php使用number_format函数截取小数的方法分析
2016/05/27 PHP
Laravel 加载第三方类库的方法
2018/04/20 PHP
解决thinkphp5未定义变量会抛出异常,页面错误,请稍后再试的问题
2019/10/16 PHP
Javascript结合css实现网页换肤功能
2009/11/02 Javascript
ajaxFileUpload.js插件支持多文件上传的方法
2014/09/02 Javascript
jQuery+HTML5实现手机摇一摇换衣特效
2015/06/05 Javascript
textarea 在浏览器中固定大小和禁止拖动的实现方法
2016/12/03 Javascript
JS回调函数深入理解
2019/10/16 Javascript
d3.js 地铁轨道交通项目实战
2019/11/27 Javascript
小程序websocket心跳库(websocket-heartbeat-miniprogram)
2020/02/23 Javascript
JavaScript实现指定数量的并发限制的示例代码
2020/03/10 Javascript
python使用Tkinter显示网络图片的方法
2015/04/24 Python
python中lambda()的用法
2017/11/16 Python
Python爬虫常用库的安装及其环境配置
2018/09/19 Python
python简易实现任意位数的水仙花实例
2018/11/13 Python
PyGame贪吃蛇的实现代码示例
2018/11/21 Python
Python程序控制语句用法实例分析
2020/01/14 Python
html5启动原生APP总结
2020/07/03 HTML / CSS
南威尔士家居商店:Leekes
2016/10/25 全球购物
香港迪士尼乐园酒店预订:Hong Kong Disneyland Hotels
2017/05/02 全球购物
德国高品质男装及配饰商城:Cultizm(Raw Denim原色牛仔裤)
2018/04/16 全球购物
澳大利亚领先的皮肤诊所:Skin Matrix(抗衰老、痤疮专家、药妆护肤)
2018/05/20 全球购物
美国亚洲时尚和美容产品的一站式网上商店:Stylevana
2019/09/05 全球购物
大学校园生活自我鉴定
2014/01/13 职场文书
学校十一活动方案
2014/02/01 职场文书
社区服务活动总结
2014/05/07 职场文书
2015年小班保育员工作总结
2015/05/27 职场文书
红色影片观后感
2015/06/18 职场文书
2016年幼儿园教师政治学习心得体会
2016/01/23 职场文书
互联网创业商业模式以及赚钱法则有哪些?
2019/10/12 职场文书
Mac M1安装mnmp (Mac+Nginx+MySQL+PHP) 开发环境
2021/03/29 PHP
Python中 range | np.arange | np.linspace三者的区别
2022/03/22 Python