W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
聚合也可以參與過濾。任何應(yīng)用于普通模型字段的 ?filter()
? (或 ?exclude()
?)會具有約束被認為是聚合的對象的效果。
當使用 ?annotate()
? 子句,過濾器具有約束計算注解的對象的效果。比如,你可以使用查詢生成一個所有書籍的注解列表,這個列表的標題以 "Django" 開頭。
>>> from django.db.models import Avg, Count
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
當使用 ?aggregate()
? 子句,過濾器將具有約束計算聚合的對象的效果。比如,你可以使用查詢生成所有標題以 "Django" 開頭的平均價格。
>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))
注解過的值也可以使用過濾器。注解的別名可以和任何其他模型字段一樣使用 ?filter()
? 和 ?exclude()
? 子句。
比如,要生成多名作者的書籍列表,可以發(fā)出這種查詢:
>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)
這個查詢生成一個注解結(jié)果集,然后生成一個基于注解的過濾器。
如果你需要兩個帶有兩個獨立的過濾器的注解,你可以在任何聚合中使用 ?filter
?語句。比如,要生成一個帶有高評價書籍的作者列表:
>>> highly_rated = Count('book', filter=Q(book__rating__gte=7))
>>> Author.objects.annotate(num_books=Count('book'), highly_rated_books=highly_rated)
結(jié)果集中的每個 Author 都有 ?num_books
?和 ?highly_rated_books
?屬性。
當開發(fā)一個涉及 ?annotate()
? 和 ?filter()
? 子句的復(fù)雜查詢時,要特別注意應(yīng)用于 ?QuerySet
?的子句的順序。
當一個 ?annotate()
? 子句應(yīng)用于查詢,會根據(jù)查詢狀態(tài)來計算注解,直到請求的注解為止。這實際上意味著 ?filter()
? 和 ?annotate()
? 不是可交換的操作。
比如:
下面就是 ?Count
?聚合的例子:
>>> a, b = Publisher.objects.annotate(num_books=Count('book', distinct=True)).filter(book__rating__gt=3.0)
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 2)
>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 1)
兩個查詢返回出版者列表,這些出版者至少有一本評分3的書,因此排除了C。
在第一個查詢里,注解優(yōu)先于過濾器,因此過濾器沒有影響注解。?distinct=True
? 用來避免一個 ?query
?bug。
第二個查詢每個發(fā)布者評分3以上的書籍數(shù)量。過濾器優(yōu)先于注解,因此過濾器約束計算注解時考慮的對象。
這里是另一個關(guān)于 Avg 聚合的例子:
>>> a, b = Publisher.objects.annotate(avg_rating=Avg('book__rating')).filter(book__rating__gt=3.0)
>>> a, a.avg_rating
(<Publisher: A>, 4.5) # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 2.5) # (1+4)/2
>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(avg_rating=Avg('book__rating'))
>>> a, a.avg_rating
(<Publisher: A>, 4.5) # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 4.0) # 4/1 (book with rating 1 excluded)
第一個查詢請求至少有一本評分3以上的書籍的出版者的書籍平均分。第二個查詢只請求評分3以上的作者書籍的平均評分。
很難憑直覺了解ORM如何將復(fù)雜的查詢集轉(zhuǎn)化為SQL查詢,因此當有疑問時,請使用 ?str(queryset.query)
? 檢查SQL,并寫大量的測試。
注解可以當做基本排序來使用。當你定義了一個 ?order_by()
? 子句,你提供的聚合可以引用任何定義為查詢中 ?annotate()
? 子句的一部分的別名。
比如,通過書籍的作者數(shù)量來對書籍的 ?QuerySet
?排序,你可以使用下面的查詢:
>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
通常,注解值會添加到每個對象上,即一個被注解的 ?QuerySet
?將會為初始 ?QuerySet
?的每個對象返回一個結(jié)果集。然而,當使用 ?values()
? 子句來對結(jié)果集進行約束時,生成注解值的方法會稍有不同。不是在原始 ?QuerySet
?中對每個對象添加注解并返回,而是根據(jù)定義在 ?values()
? 子句中的字段組合先對結(jié)果進行分組,再對每個單獨的分組進行注解,這個注解值是根據(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é)果中;兩個作者的所有評分都將被計算為一個平均分。
和使用 ?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
?已經(jīng)明確包含在返回的值列表中。這是必需的,因為 ?values()
? 和 ?annotate()
? 子句的順序。
如果 ?values()
? 子句在 ?annotate()
? 子句之前,任何注解將自動添加在結(jié)果集中。然而,如果 ?values()
? 子句應(yīng)用在 ?annotate()
? 子句之后,則需要顯式包含聚合列。
在選擇輸出數(shù)據(jù)時使用查詢集的 ?order_by()
? 部分中提到的字段,即使在 ?values()
? 調(diào)用中沒有另外指定它們也是如此。 這些額外的字段用于將“?like
?”的結(jié)果組合在一起,它們可以使原本相同的結(jié)果行看起來是分開的。 尤其是在計算事物時,這一點會出現(xiàn)。
舉個例子,假設(shè)你有這樣的模型:
from django.db import models
class Item(models.Model):
name = models.CharField(max_length=10)
data = models.IntegerField()
如果您想計算每個不同數(shù)據(jù)值在有序查詢集中出現(xiàn)的次數(shù),您可以試試這個:
items = Item.objects.order_by('name')
# Warning: not quite correct!
items.values('data').annotate(Count('id'))
它將按 ?Item
?對象的公共數(shù)據(jù)值對它們進行分組,然后計算每組中 ?id
?值的數(shù)量。 除非它不會完全工作。 按名稱排序也將在分組中發(fā)揮作用,因此此查詢將按不同的(數(shù)據(jù),名稱)對分組,這不是您想要的。 相反,您應(yīng)該構(gòu)造這個查詢集:
items.values('data').annotate(Count('id')).order_by()
清除任何查詢中的排序。你也可以通過 ?data
?排序,沒有任何有害影響,因為它已經(jīng)在查詢中發(fā)揮了作用。
這個行為與 ?distinct()
? 的行為相同,一般規(guī)則是一樣的:通常情況下,你不希望額外的列在結(jié)果中發(fā)揮作用,因此要清除排序,或者至少確保它只限于您在 ?values()
? 調(diào)用中選擇的那些字段。
你也可以在注解結(jié)果上生成聚合。當你定義 ?aggregate()
? 子句時,你提供的聚合可以引用任何定義在查詢中 ?annotate()
? 子句的別名。
比如,如果你想計算每本書的平均作者數(shù),首先使用作者數(shù)注解書籍集合,然后引用注解字段聚合作者數(shù):
>>> from django.db.models import Avg, Count
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: