Django2.2丨聚合

Django数据库抽象API描述了使用Django queries来增删改查单个对象的方法。

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)
速查表
# Total number of books.
>>> Book.objects.count()
2452

# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73

# Average price across all books.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}

# Difference between the highest priced book and the average price of all books.
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
...     price_diff=Max('price', output_field=FloatField()) - Avg('price'))
{'price_diff': 46.85}

# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.

# Each publisher, each with a count of books as a "num_books" attribute.
>>> 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

# Each publisher, with a separate count of books with a rating above and 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

# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323
在QuerySet上生成聚合

Django提供了两种生成聚合的方法。

第一种是从整个QuerySet生成汇总值。例想要计算所有在售书的平均价格

查询所有图书集合的语句

>>> 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()是QuerySet的一个终端子句,使用后将返回“名称-值”的字典,其中名称就是聚合值的标志,值就是计算出的聚合结果。

名称是根据字段名和聚合函数而自动生成。如果像指定名称可以通过使用子句时指定

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

如果想生成更多的聚合内容,只需在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')}
为QuerySet中的每一个条目生成聚合

生成值的汇总的另一个办法是为QuerySet的每一个对象生成独立汇总。

例:如果想检索书籍列表, 并查询每本书有多少个作者。每一本书与作者有多对多的关系,想在QuerySet中为每一本书总结这个关系。

使用annotate()子句可以生成每一个对象的汇总。

示例

  • 注解(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

同样可以在注解时自定字段名

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

注:annotate()不是终端子句。输出的是QuerySet。

组合多个聚合

使用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
连接和聚合

在聚合函数里面指定聚合的字段时,Django允许在过滤相关字段的时候使用相同的双下划线表示法。Django将处理任何需要检索和聚合的关联值得表连接。

示例

  • 想要寻找每个书店提供的书籍价格区间
>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

过程是让Django去检索Store模型,连接(通过多对多关系)Book模型,并且聚合书籍模型的价格字段来获取最大最小值。

使用aggregate()从句

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))

Join链可以根据需求尽可能深的查询

示例

  • 提取所出售的书籍中最年轻的作者年龄
>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
反向关系

类似于跨关系查询

示例

  • 要求所有出版社,注解他们各自的图书库存总数
>>> from django.db.models import Avg, Count, Min, Sum
>>> Publisher.objects.annotate(Count('book'))

查询结果里的每一个Publisher会有多余的属性(book__count)

  • 找出最古老的一本
>>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值