• 【Django】聚合查询


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

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    count()

    查询图书总数

    >>> Book.objects.count()
    2452
    
    • 1
    • 2

    出版社名为“WordPress”的图书总数

    >>> Book.objects.filter(publisher__name="WordPress").count()
    73
    
    • 1
    • 2

    aggregate()

    aggregate()方法是在整个 QuerySet 上生成聚合值。
    所有书的均价,如果没有书则返回默认值0

    >>> from django.db.models import Avg
    >>> Book.objects.aggregate(Avg("price", default=0))
    {'price__avg': 34.35}
    
    • 1
    • 2
    • 3

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

    >>> from django.db.models import Max
    >>> Book.objects.aggregate(Max("price", default=0))
    {'price__max': Decimal('81.20')}
    
    • 1
    • 2
    • 3

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

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

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

    >>> 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')}
    
    • 1
    • 2
    • 3

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

    >>> from django.db.models import FloatField
    >>> Book.objects.aggregate(
    ...     price_diff=Max("price", output_field=FloatField()) - Avg("price")
    ... )
    {'price_diff': 46.85}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    annotate()

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

    aggregate() 相同点,注释的名称是从聚合函数的名称和被聚合字段的名称自动派生的。您可以通过在指定注释时提供别名来覆盖这个默认名称。
    aggregate() 不同的是,annotate() 不是终端子句。annotate() 子句的输出就是 QuerySet;这个 QuerySet 可以像其他 QuerySet 一样进行操作,包括 filter(), order_by() ,甚至可以对 annotate() 进行额外调用。

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

    >>> 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    >>> 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    >>> pubs = Publisher.objects.annotate(num_books=Count("book")).order_by("-num_books")[:5]
    >>> pubs[0].num_books
    1323
    
    • 1
    • 2
    • 3

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

    >>> 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    >>> q = Book.objects.annotate(
    ...     Count("authors", distinct=True), Count("store", distinct=True)
    ... )
    >>> q[0].authors__count
    2
    >>> q[0].store__count
    3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    中间件Weblogic12.2.1.4与iServer 8C 10i兼容性问题解决过程分享
    Post与get的请求过程
    【老生谈算法】matlab实现控制系统稳定性——控制系统
    Jenkins插件安装失败时这么做就搞定啦
    R语言绘制精美图形 | 火山图 | 学习笔记
    【Python爬虫】解析xpath——尚硅谷
    1.(vue3.x+vite)封装组件
    C专家编程 第4章 令人震惊的事实:数组和指针并不相同 4.2 我的代码为什么无法运行
    分库分表(2)——动态数据源实践
    Web3对法律的需求
  • 原文地址:https://blog.csdn.net/chengyikang20/article/details/136462042