django数据库操作-查03-聚合函数和排序函数

669 阅读2分钟

聚合函数和排序函数

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

在本指南(以及参考资料)中,我们将参考以下模型,

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)

1. 聚合函数

aggregate()

使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg平均,Count数量,Max最大,Min最小,Sum求和,被定义在django.db.models中。

下面是根据以上模型执行常见的聚合查询:

>>> Book.objects.count()
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
>>> Book.objects.all().aggregate(Avg('price'))
>>> Book.objects.all().aggregate(Max('price'))
>>> Book.objects.aggregate(
...     price_diff=Max('price', output_field=FloatField()) - Avg('price'))
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> above_5 = Count('book', filter=Q(book__rating__gt=5))
>>> below_5 = Count('book', filter=Q(book__rating__lte=5))

注意count函数的返回值是一个数字。

在 QuerySet 上生成聚合

Django 提供了两种生成聚合的方法。第一种方法是从整个 QuerySet 生成汇总值。比如你想要计算所有在售书的平均价格。Django 的查询语法提供了一种用来描述所有图书集合的方法:

>>> Book.objects.all()
  • 可以通过在 QuerySet 后添加 aggregate() 子句来计算 QuerySet 对象的汇总值。
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
  • 本例中的 all() 是多余的,所以可以简化成这样的:
>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

传递给 aggregate() 的参数描述了我们想要计算的聚合值。在这个例子里,要计算的就是 Book 模型上的 price 字段的平均值。可用的聚合函数列表可以在 QuerySet reference 中找到。

annotate()

为 QuerySet 中的每一个条目生成聚合 与 aggregate() 一样,注解的名称是根据聚合函数和被聚合的字段名自动生成的。当你在指定注解的时候,你可以通过提供一个别名重写这个默认名:

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

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

2. 排序

使用order_by对结果进行排序

order_by()

order_by可以当做基本排序来使用。当你定义了一个 order_by() 子句,你提供的聚合可以引用任何定义为查询中 annotate() 子句的一部分的别名。

比如,通过书籍的作者数量来对书籍的 QuerySet 排序,你可以使用下面的查询:

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

与默认排序或 order_by() 交互

2.2 版后已移除:从Django 3.1 中开始,模型的 Meta.ordering 中的排序不会使用在 GROUP BY 查询,比如 .annotate().values() 。从 Django 2.2 开始,这些查询发出一个弃用警告,指示要在查询集中添加一个显式的 order_by() 来静默警告。