【Django】聚合查询

本篇以下面的模型为基础进行讨论,根据查询目标列出示例代码,和示例结果。

python 复制代码
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)

count()

查询图书总数

python 复制代码
>>> Book.objects.count()
2452

出版社名为"WordPress"的图书总数

python 复制代码
>>> Book.objects.filter(publisher__name="WordPress").count()
73

aggregate()

aggregate()方法是在整个 QuerySet 上生成聚合值。

所有书的均价,如果没有书则返回默认值0

python 复制代码
>>> from django.db.models import Avg
>>> Book.objects.aggregate(Avg("price", default=0))
{'price__avg': 34.35}

所有书中最贵的价格,如果没有书则返回默认值0

python 复制代码
>>> from django.db.models import Max
>>> Book.objects.aggregate(Max("price", default=0))
{'price__max': Decimal('81.20')}

aggregate() 是一个 QuerySet 的终端子句,当调用时,它返回一个字典。名称是聚合值的标识符;值是计算得到的聚合值。名称是从字段名称和聚合函数自动生成的。如果您想手动指定聚合值的名称,可以在指定聚合子句时提供该名称:

python 复制代码
>>> Book.objects.aggregate(average_price=Avg("price"))
{'average_price': 34.35}

如果您想生成多个聚合值,可以向 aggregate() 子句添加另一个参数。因此,如果我们还想知道所有书的最高价和最低价,可以发出以下查询:

python 复制代码
>>> 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')}

书中最贵价格和平均价格的差值

python 复制代码
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
...     price_diff=Max("price", output_field=FloatField()) - Avg("price")
... )
{'price_diff': 46.85}

annotate()

使用 annotate() 子句可以生成每一个对象的汇总。当指定 annotate() 子句,QuerySet 中的每一个对象将对指定值进行汇总。

aggregate() 相同点,注释的名称是从聚合函数的名称和被聚合字段的名称自动派生的。您可以通过在指定注释时提供别名来覆盖这个默认名称。

aggregate() 不同的是,annotate() 不是终端子句。annotate() 子句的输出就是 QuerySet;这个 QuerySet 可以像其他 QuerySet 一样进行操作,包括 filter(), order_by() ,甚至可以对 annotate() 进行额外调用。

查询每个出版社的图书总数,并添加到"num_books"属性值中

python 复制代码
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count("book"))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73

查询每个出版社评分大于5的书数量和小于等于5的书的数量,并添加到"above_5"和"below_5"的属性值中

python 复制代码
>>> from django.db.models import Q
>>> above_5 = Count("book", filter=Q(book__rating__gt=5))
>>> below_5 = Count("book", filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12

找出书籍数量排名前5的出版社,并添加到"num_books"属性值中

python 复制代码
>>> pubs = Publisher.objects.annotate(num_books=Count("book")).order_by("-num_books")[:5]
>>> pubs[0].num_books
1323

还有个地方需要注意:使用 annotate() 组合多个聚合将产生错误的结果,因为它使用连接(joins)而不是子查询,下面是一个错误示例:

python 复制代码
>>> book = Book.objects.first()
>>> book.authors.count()
2
>>> book.store_set.count()
3
>>> q = Book.objects.annotate(Count("authors"), Count("store"))
>>> q[0].authors__count
6
>>> q[0].store__count
6

对大部分聚合来说,没办法避免这个问题,但是,Count 聚合可以使用 distinct 参数来避免:

python 复制代码
>>> q = Book.objects.annotate(
...     Count("authors", distinct=True), Count("store", distinct=True)
... )
>>> q[0].authors__count
2
>>> q[0].store__count
3
相关推荐
小光学长2 分钟前
基于flask+vue框架的的医院预约挂号系统i1616(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库
听封10 分钟前
✨ 索引有哪些缺点以及具体有哪些索引类型
数据库·mysql
利瑞华15 分钟前
数据库索引:缺点与类型全解析
数据库·oracle
V+zmm1013418 分钟前
自驾游拼团小程序的设计与实现(ssm论文源码调试讲解)
java·数据库·微信小程序·小程序·毕业设计
ChinaRainbowSea29 分钟前
1. Linux下 MySQL 的详细安装与使用
linux·数据库·sql·mysql·adb
jay丿2 小时前
Redis 中列表(List)常见命令详解
数据库·redis·list
小林熬夜学编程2 小时前
【MySQL】第八弹---全面解析数据库表的增删改查操作:从创建到检索、排序与分页
linux·开发语言·数据库·mysql·算法
LCY1333 小时前
websocket与django
websocket·django·sqlite
RainbowSea3 小时前
4. MySQL 逻辑架构说明
数据库·sql·mysql
AI趋势预见3 小时前
FinRL-DeepSeek: 大语言模型赋能的风险敏感型强化学习交易代理
数据库·人工智能·语言模型·自然语言处理·金融