1. 程式人生 > >Django 1.10中文文件-聚合

Django 1.10中文文件-聚合

正文

Django 資料庫抽象API 描述了使用Django 查詢來增刪查改單個物件的方法。 然而,有時候你要獲取的值需要根據一組物件聚合後才能得到。 這個主題指南描述瞭如何使用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) num_awards = models.IntegerField() 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) pubdate = models.DateField() class Store(models.Model): name = models.CharField(max_length=300) books = models.ManyToManyField(Book) registered_users = models.PositiveIntegerField()
回到頂部

速查表

下面是在上面的模型上如何執行常見的聚合查詢:

# book 總數.
>>> Book.objects.count()
2452
# publisher=BaloneyPress的book總數.
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73
# 所有book的平均價格.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
# 所有book的最高價格
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}
# 每頁均價
>>> from django.db.models import F, FloatField, Sum
>>> Book.objects.all().aggregate(
...    price_per_page=Sum(F('price')/F('pages'), output_field=FloatField()))
{'price_per_page': 0.4470664529184653}
# 下面的所有查詢都涉及到遍歷 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
# 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 生成統計值。 比如,你想要計算所有在售書的平均價錢。Django的查詢語法提供了一種方式描述所有圖書的集合。:

>>> Book.objects.all()

我們需要在 QuerySet 物件上計算出彙總的值。這可以通過在 QuerySet 後面新增 aggregate() 子句來實現:

>>> 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 欄位的平均值。 查詢集參考 有所有的聚合函式。

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() 子句被指定之後, QuerySet 中的每個物件都會被註上特定的值。

annotate(註解)的語法都和 aggregate() 子句相同。 annotate() 的每個引數都描述了將要被計算的聚合值。

比如,給圖書新增作者數量的註解:

# Build an annotated queryset
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors'))
# 查詢queryset中的第一個物件
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# 查詢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