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

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

聚合

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

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

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ǔ)上,進(jìn)行一般的聚合查詢的方法:

# 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提供了兩種生成聚合的方法。第一種方法是從整個(gè)查詢集生成統(tǒng)計(jì)值。比如,你想要計(jì)算所有在售書的平均價(jià)錢。Django的查詢語法提供了一種方式描述所有圖書的集合。

>>> Book.objects.all()

我們需要在QuerySet.對(duì)象上計(jì)算出總價(jià)格。這可以通過在QuerySet后面附加aggregate() 子句來完成。

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

all()在這里是多余的,所以可以簡(jiǎn)化為:

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

aggregate()子句的參數(shù)描述了我們想要計(jì)算的聚合值,在這個(gè)例子中,是Book 模型中price字段的平均值。查詢集參考中列出了聚合函數(shù)的列表。

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

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

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

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

為查詢集的每一項(xiàng)生成聚合

生成匯總值的第二種方法,是為QuerySet中每一個(gè)對(duì)象都生成一個(gè)獨(dú)立的匯總值。比如,如果你在檢索一列圖書,你可能想知道有多少作者寫了每一本書。每本書和作者是多對(duì)多的關(guān)系。我們想要匯總QuerySet.中每本書里的這種關(guān)系。

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

這些注解的語法都和aggregate()子句所使用的相同。annotate()的每個(gè)參數(shù)都描述了將要被計(jì)算的聚合。比如,給圖書添加作者數(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ù)聚合函式的名稱和聚合字段的名稱得到的。你可以在指定注解時(shí),為默認(rèn)名稱提供一個(gè)別名:

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

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

有任何疑問的話,請(qǐng)檢查 SQL query!

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

例如,在annotate() 中混入多個(gè)聚合將會(huì)得出錯(cuò)誤的結(jié)果,因?yàn)槎鄠€(gè)表上做了交叉連接,導(dǎo)致了多余的行聚合。

連接和聚合

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

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

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

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

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

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

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

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

>>> 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é)果中的每一個(gè)Publisher都會(huì)包含一個(gè)額外的屬性叫做book__count。

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

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

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

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

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

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

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

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

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

聚合和其他查詢集子句

filter() 和 exclude()

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

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

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

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

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

對(duì)注解過濾

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

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

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

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

annotate() 的順序

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

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

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

另一個(gè)查詢:

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

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

order_by()

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

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

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

values()

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

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

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

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

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

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

在這個(gè)例子中,作者會(huì)按名稱分組,所以你只能得到某個(gè)唯一的作者分組的注解值。 這意味著如果你有兩個(gè)作者同名,那么他們?cè)靖髯缘牟樵兘Y(jié)果將被合并到同一個(gè)結(jié)果中;兩個(gè)作者的所有評(píng)分都將被計(jì)算為一個(gè)平均分。

annotate() 的順序

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

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

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

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

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

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

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

與默認(rèn)排序或order_by()交互

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

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

from django.db import models

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

    class Meta:
        ordering = ["name"]

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

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

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

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

...這樣就清空了查詢中的所有排序項(xiàng)。 你也可以在其中使用 data ,這樣并不會(huì)有副作用,這是因?yàn)椴樵兎纸M中只有這么一個(gè)角色了。

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

注意

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

聚合注解

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

例如,如果你想計(jì)算每本書平均有幾個(gè)作者,你先用作者總數(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}
上一篇:安全問題歸檔下一篇:Widgets