Django4 中文入门教程 Django4.0 聚合-在QuerySet中的每一个条目生成聚合

2024-02-25 开发教程 Django4 中文入门教程 匿名 5

生成值的汇总的另一个办法是为 ​QuerySet ​的每一个对象生成独立汇总。比如,如果你想检索书籍列表,你可能想知道每一本书有多少作者。每一本书与作者有多对多的关系;我们想在 ​QuerySet ​中为每一本书总结这个关系。

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

这些语法与用于 ​aggregate()​ 子句的语法相同。 annotate() 的每个参数都描述了一个要计算的聚合。 例如,用作者数量注释书籍:

# Build an annotated queryset
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors'))
# Interrogate the first object in the queryset
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

与 ​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()​ 进行额外调用。

组合多个聚合

将多个聚合与​annotate() ​组合会产生错误的结果,因为使用的是连接而不是子查询:

>>> 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 ​参数来避免:

>>> q = Book.objects.annotate(Count('authors', distinct=True), Count('store', distinct=True))
>>> q[0].authors__count
2
>>> q[0].store__count
3