在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 聚合
編寫自定義存儲系統(tǒng)
高級
編寫你的第一個Django應(yīng)用,第5部分
視圖層
Django管理文檔生成器
編寫你的第一個 Django 程序 第3部分
編寫你的第一個Django應(yīng)用,第6部分
模型層
中間件
測試
數(shù)據(jù)庫訪問優(yōu)化
文件上傳
中間件
驗證器
基于類的內(nèi)建通用視圖
部署靜態(tài)文件
使用Django認證系統(tǒng)
高級教程:如何編寫可重用的應(yīng)用
Model 類參考
Django 初探
使用Django輸出PDF
模型的實例
模型
文件上傳
進行原始的sql查詢
面向程序員
中間件
編寫數(shù)據(jù)庫遷移
TemplateResponse 和 SimpleTemplateResponse
異常
Django中的測試
基礎(chǔ)
管理器
File對象
URL調(diào)度器
加密簽名
國際化和本地化
日志
查詢集
django-admin 和 manage.py
使用基于類的視圖處理表單
聚合
內(nèi)建基于類的視圖的API
如何使用會話
部署 Django
其它
其它核心功能
高級
Django中的密碼管理
模型元選項
按需內(nèi)容處理
查找 API 參考
高級
Django 的快捷函數(shù)
Django 的設(shè)置
Admin
開發(fā)過程
新手入門
基于類的視圖
模型實例參考
信號
表單素材 ( <code>Media</code> 類)
自定義查找
常見的網(wǎng)站應(yīng)用工具
模型
django.contrib.humanize
Django模版語言
點擊劫持保護
管理操作
編寫你的第一個 Django 程序 第2部分
Django安全
模式編輯器
多數(shù)據(jù)庫
部署
基于類的視圖
內(nèi)建的視圖
視圖裝飾器
面向設(shè)計師
編寫視圖
應(yīng)用程序
如何使用WSGI 部署
參考
表單 API
文件儲存API
認證
國際化和本地化
錯誤報告
基礎(chǔ)
基礎(chǔ)
將遺留數(shù)據(jù)庫整合到Django
教程
Django異常
編寫你的第一個 Django 程序 第4部分
遷移
分頁
重定向應(yīng)用
表單
從零開始
為模型提供初始數(shù)據(jù)
設(shè)置
使用Django輸出CSV
關(guān)聯(lián)對象參考
使用表單
Django 中的用戶認證
快速安裝指南
安全問題歸檔
數(shù)據(jù)庫函數(shù)
編寫自定義的django-admin命令
高級教程
管理文件
格式本地化
基于類的通用視圖 —— 索引
安全
系統(tǒng)檢查框架
為Django編寫首個補丁
模板層
Widgets
編寫你的第一個 Django 程序 第1部分
執(zhí)行查詢

聚合

Django數(shù)據(jù)庫抽象API描述了使用Django查詢來增刪查改單個對象的方法。然而,你有時候會想要獲取從一組對象導出的值或者是聚合一組對象。這份指南描述了通過Django查詢來生成和返回聚合值的方法。

整篇指南我們都將引用以下模型。這些模型用來記錄多個網(wǎng)上書店的庫存。

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()

速查表

急著用嗎?以下是在上述模型的基礎(chǔ)上,進行一般的聚合查詢的方法:

# 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')}

# Cost per page
>>> Book.objects.all().aggregate(
...    price_per_page=Sum(F('price')/F('pages'), output_field=FloatField()))
{'price_per_page': 0.4470664529184653}

# All the following queries involve traversing the Book<->Publisher
# many-to-many relationship backward

# 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
[<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

在查詢集上生成聚合

Django提供了兩種生成聚合的方法。第一種方法是從整個查詢集生成統(tǒng)計值。比如,你想要計算所有在售書的平均價錢。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()子句的參數(shù)描述了我們想要計算的聚合值,在這個例子中,是Book 模型中price字段的平均值。查詢集參考中列出了聚合函數(shù)的列表。

aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數(shù)的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。

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

如果你希望生成不止一個聚合,你可以向aggregate()子句中添加另一個參數(shù)。所以,如果你也想知道所有圖書價格的最大值和最小值,可以這樣查詢:

>>> 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中每一個對象都生成一個獨立的匯總值。比如,如果你在檢索一列圖書,你可能想知道有多少作者寫了每一本書。每本書和作者是多對多的關(guān)系。我們想要匯總QuerySet.中每本書里的這種關(guān)系。

逐個對象的匯總結(jié)果可以由annotate()子句生成。當annotate()子句被指定之后,QuerySet中的每個對象都會被注上特定的值。

這些注解的語法都和aggregate()子句所使用的相同。annotate()的每個參數(shù)都描述了將要被計算的聚合。比如,給圖書添加作者數(shù)量的注解:

# 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()一樣,注解的名稱也根據(jù)聚合函式的名稱和聚合字段的名稱得到的。你可以在指定注解時,為默認名稱提供一個別名:

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

與 aggregate() 不同的是, annotate() 不是一個終止子句。annotate()子句的返回結(jié)果是一個查詢集 (QuerySet);這個 QuerySet可以用任何QuerySet方法進行修改,包括 filter(), order_by(), 甚至是再次應(yīng)用annotate()。

有任何疑問的話,請檢查 SQL query!

要想弄清楚你的查詢到底發(fā)生了什么,可以考慮檢查你QuerySet的 query 屬性。

例如,在annotate() 中混入多個聚合將會得出錯誤的結(jié)果,因為多個表上做了交叉連接,導致了多余的行聚合。

連接和聚合

至此,我們已經(jīng)了解了作用于單種模型實例的聚合操作, 但是有時,你也想對所查詢對象的關(guān)聯(lián)對象進行聚合。

在聚合函式中指定聚合字段時,Django 允許你使用同樣的 雙下劃線 表示關(guān)聯(lián)關(guān)系,然后 Django 在就會處理要讀取的關(guān)聯(lián)表,并得到關(guān)聯(lián)對象的聚合。

例如,要得到每個書店的價格區(qū)別,可以使用如下注解:

>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

這段代碼告訴 Django 獲取書店模型,并連接(通過多對多關(guān)系)圖書模型,然后對每本書的價格進行聚合,得出最小值和最大值。

同樣的規(guī)則也用于 aggregate() 子句。如果你想知道所有書店中最便宜的書和最貴的書價格分別是多少:

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

關(guān)系鏈可以按你的要求一直延伸。 例如,想得到所有作者當中最小的年齡是多少,就可以這樣寫:

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

遵循反向關(guān)系

和 跨關(guān)系查找的方法類似,作用在你所查詢的模型的關(guān)聯(lián)模型或者字段上的聚合和注解可以遍歷"反轉(zhuǎn)"關(guān)系。關(guān)聯(lián)模型的小寫名稱和雙下劃線也用在這里。

例如,我們可以查詢所有出版商,并注上它們一共出了多少本書(注意我們?nèi)绾斡?'book'指定Publisher -> Book 的外鍵反轉(zhuǎn)關(guān)系):

>>> from django.db.models import Count, Min, Sum, Avg
>>> Publisher.objects.annotate(Count('book'))

QuerySet結(jié)果中的每一個Publisher都會包含一個額外的屬性叫做book__count。

我們也可以按照每個出版商,查詢所有圖書中最舊的那本:

>>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))

(返回的字典會包含一個鍵叫做 'oldest_pubdate'。如果沒有指定這樣的別名,它會更長一些,像 'bookpubdatemin'。)

這不僅僅可以應(yīng)用掛在外鍵上面。還可以用到多對多關(guān)系上。例如,我們可以查詢每個作者,注上它寫的所有書(以及合著的書)一共有多少頁(注意我們?nèi)绾问褂?'book'來指定Author -> Book的多對多的反轉(zhuǎn)關(guān)系):

>>> Author.objects.annotate(total_pages=Sum('book__pages'))

(每個返回的QuerySet中的Author 都有一個額外的屬性叫做total_pages。如果沒有指定這樣的別名,它會更長一些,像 bookpagessum。)

或者查詢所有圖書的平均評分,這些圖書由我們存檔過的作者所寫:

>>> Author.objects.aggregate(average_rating=Avg('book__rating'))

(返回的字典會包含一個鍵叫做'averagerating'。如果沒有指定這樣的別名,它會更長一些,像'bookrating__avg'。)

聚合和其他查詢集子句

filter() 和 exclude()

聚合也可以在過濾器中使用。 作用于普通模型字段的任何 filter()(或 exclude()) 都會對聚合涉及的對象進行限制。

使用annotate() 子句時,過濾器有限制注解對象的作用。例如,你想得到以 "Django" 為書名開頭的圖書作者的總數(shù):

>>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

使用aggregate()子句時,過濾器有限制聚合對象的作用。例如,你可以算出所有以 "Django" 為書名開頭的圖書平均價格:

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

對注解過濾

注解值也可以被過濾。 像使用其他模型字段一樣,注解也可以在filter()和exclude() 子句中使用別名。

例如,要得到不止一個作者的圖書,可以用:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

這個查詢首先生成一個注解結(jié)果,然后再生成一個作用于注解上的過濾器。

annotate() 的順序

編寫一個包含 annotate() 和 filter() 子句的復雜查詢時,要特別注意作用于 QuerySet的子句的順序。

當一個annotate() 子句作用于某個查詢時,要根據(jù)查詢的狀態(tài)才能得出注解值,而狀態(tài)由 annotate() 位置所決定。以這就導致filter() 和 annotate() 不能交換順序,下面兩個查詢就是不同的:

>>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)

另一個查詢:

>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

兩個查詢都返回了至少出版了一本好書(評分大于 3 分)的出版商。 但是第一個查詢的注解包含其該出版商發(fā)行的所有圖書的總數(shù);而第二個查詢的注解只包含出版過好書的出版商的所發(fā)行的圖書總數(shù)。 在第一個查詢中,注解在過濾器之前,所以過濾器對注解沒有影響。 在第二個查詢中,過濾器在注解之前,所以,在計算注解值時,過濾器就限制了參與運算的對象的范圍。

order_by()

注解可以用來做為排序項。 在你定義 order_by() 子句時,你提供的聚合可以引用定義的任何別名做為查詢中 annotate()子句的一部分。

例如,根據(jù)一本圖書作者數(shù)量的多少對查詢集 QuerySet進行排序:

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

values()

通常,注解會添加到每個對象上 —— 一個被注解的QuerySet會為初始QuerySet的每個對象返回一個結(jié)果集。但是,如果使用了values()子句,它就會限制結(jié)果中列的范圍,對注解賦值的方法就會完全不同。不是在原始的 QuerySet返回結(jié)果中對每個對象中添加注解,而是根據(jù)定義在values() 子句中的字段組合對先結(jié)果進行唯一的分組,再根據(jù)每個分組算出注解值, 這個注解值是根據(jù)分組中所有的成員計算而得的:

例如,考慮一個關(guān)于作者的查詢,查詢出每個作者所寫的書的平均評分:

>>> Author.objects.annotate(average_rating=Avg('book__rating'))

這段代碼返回的是數(shù)據(jù)庫中所有的作者以及他們所著圖書的平均評分。

但是如果你使用了values()子句,結(jié)果是完全不同的:

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

在這個例子中,作者會按名稱分組,所以你只能得到某個唯一的作者分組的注解值。 這意味著如果你有兩個作者同名,那么他們原本各自的查詢結(jié)果將被合并到同一個結(jié)果中;兩個作者的所有評分都將被計算為一個平均分。

annotate() 的順序

和使用 filter() 子句一樣,作用于某個查詢的annotate() 和 values() 子句的使用順序是非常重要的。如果values() 子句在 annotate() 之前,就會根據(jù) values() 子句產(chǎn)生的分組來計算注解。

但是,如果 annotate() 子句在 values()子句之前,就會根據(jù)整個查詢集生成注解。在這種情況下,values() 子句只能限制輸出的字段范圍。

舉個例子,如果我們互換了上個例子中 values()和 annotate() 子句的順序:

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

這段代碼將給每個作者添加一個唯一的字段,但只有作者名稱和average_rating 注解會返回在輸出結(jié)果中。

你也應(yīng)該注意到 average_rating 顯式地包含在返回的列表當中。之所以這么做的原因正是因為values() 和 annotate() 子句。

如果 values() 子句在 annotate() 子句之前,注解會被自動添加到結(jié)果集中;但是,如果 values() 子句作用于annotate() 子句之后,你需要顯式地包含聚合列。

與默認排序或order_by()交互

在查詢集中的order_by() 部分(或是在模型中默認定義的排序項) 會在選擇輸出數(shù)據(jù)時被用到,即使這些字段沒有在values() 調(diào)用中被指定。這些額外的字段可以將相似的數(shù)據(jù)行分在一起,也可以讓相同的數(shù)據(jù)行相分離。在做計數(shù)時,就會表現(xiàn)地格外明顯:

通過例子中的方法,假設(shè)有一個這樣的模型:

from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=10)
    data = models.IntegerField()

    class Meta:
        ordering = ["name"]

關(guān)鍵的部分就是在模型默認排序項中設(shè)置的name字段。如果你想知道每個非重復的data值出現(xiàn)的次數(shù),可以這樣寫:

# Warning: not quite correct!
Item.objects.values("data").annotate(Count("id"))

...這部分代碼想通過使用它們公共的 data 值來分組 Item對象,然后在每個分組中得到 id 值的總數(shù)。但是上面那樣做是行不通的。這是因為默認排序項中的 name也是一個分組項,所以這個查詢會根據(jù)非重復的 (data, name) 進行分組,而這并不是你本來想要的結(jié)果。所以,你應(yīng)該這樣改寫:

Item.objects.values("data").annotate(Count("id")).order_by()

...這樣就清空了查詢中的所有排序項。 你也可以在其中使用 data ,這樣并不會有副作用,這是因為查詢分組中只有這么一個角色了。

這個行為與查詢集文檔中提到的 distinct() 一樣,而且生成規(guī)則也一樣:一般情況下,你不想在結(jié)果中由額外的字段扮演這個角色,那就清空排序項,或是至少保證它僅能訪問 values()中的字段。

注意

你可能想知道為什么 Django 不刪除與你無關(guān)的列。主要原因就是要保證使用 distinct()和其他方法的一致性。Django 永遠不會 刪除你所指定的排序限制(我們不能改動那些方法的行為,因為這會違背 API stability 原則)。

聚合注解

你也可以在注解的結(jié)果上生成聚合。 當你定義一個 aggregate() 子句時,你提供的聚合會引用定義的任何別名做為查詢中 annotate() 子句的一部分。

例如,如果你想計算每本書平均有幾個作者,你先用作者總數(shù)注解圖書集,然后再聚合作者總數(shù),引入注解字段:

>>> from django.db.models import Count, Avg
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}