django 統計

統計

Django 數據庫抽象 API 描述瞭如何建立、檢索、更新和刪除獨立的對象。可是,有時你會須要處理一些有關對象的集合的統計。本文描述如何使用 Django 查詢來處理統計。 html

本文咱們將使用如下模型。這些模型用於在線書店圖書清單: git

class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() friends = models.ManyToManyField('self', blank=True) class Publisher(models.Model): name = models.CharField(max_length=300) num_awards = models.IntegerField() class Book(models.Model): isbn = models.CharField(max_length=9) 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)

生成整個查詢集的統計

Django 提供兩種方法來產生統計。第一種方法是產生整個 查詢集 的統計。假設咱們要統計全部書的平均價格。 Djnago 中查詢全部書的語句爲: 數據庫

>>> Book.objects.all()

在這個語句後加上一個 aggregate() 子句就好了: django

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

上例中的 all() 是多餘的。因此能夠簡寫爲: api

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

aggregate() 子句的參數表明咱們要統計的內容,本例中咱們要統計 Book 模型中 price 字段的平均值。 查詢集參考 中能夠找到完整的統計函數列表。 ide

aggregate() 是一個 查詢集 的未端子句,調用後會返回一個由名稱-值配對組成的字典。名稱是指統計的名稱,值就是統計的值。名稱由字段名稱加上函數名自動組成。若是你想手動指定統計名稱,能夠象下例在統計子句中定義: 函數

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

若是你想要生成多個統計,那麼只要在統計子句後加上另外的統計子句就能夠了。例如,若是要計算全部書價中最高價和最低價,能夠這樣寫: ui

>>> from django.db.models import Avg, Max, Min, Count >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

生成查詢集中每個項目的統計

第二種方法是爲 查詢集 中每一個獨立的對象生成統計。例如,當你檢索一個書單時,可能想知道每本書有幾個做者。每本書與每一個做者之間是一個多對多的關係,咱們要爲每本書總結這個關係。 spa

要產生每一個對象的統計可使用 annotate() 子句。當定義一個 annotate() 子句後, 查詢集 中的每一個對象就能夠與特定值關聯,至關於每一個對象有一個 「註釋」。 .net

這種註釋的語法與 aggregate() 相同。 annotate() 的每一個參數表明一個統計。例如,要計算每本書的做者人數:

# Build an annotated queryset
>>> 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() 同樣,統計的名稱自動由字段名和函數名組成。你也能夠在定義統計時指定名稱:

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

與 aggregate() 不一樣的是 annotate() 不是 一個未端子句。 annotate() 子句的輸出是一個 查詢集 。 這個 查詢集 能夠和其餘查詢集同樣操做,包括 filter() 、order_by 或者甚至再調用另外一個 annotate() 。

聯合和統計

至此,咱們統計的對象都是被查詢的模塊自己的字段。可是,有時咱們要統計的是被查詢模塊的相關聯的模塊字段。

在統計函數中定義字段時,可使用與過濾器中用於指定關聯字段的 雙下劃線符號 。經過這種方法, Django 會自動使用聯合來統計相關聯的字段。

例如,要統計每一個書店中書的價格範圍:

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

上面的例子告訴 Django 檢索 Store 模型,聯合(經過多對多關係) Book 模型,而且統計 book 模型中的價格字段,計算最大值和最小值。

aggergate() 子句適用一樣規則。若是你想知道全部書店中書的最高價和最低價,能夠這樣:

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

聯合的深度是無限的。例如,要統計全部書的做者的最小年齡,你能夠這樣:

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

統計和其餘查詢子句

filter() 和 exclude()

在過濾器中也可使用統計。任何用於通常模型的 filter() (或 exclude() )也可與統計聯用。

當與 annotate() 子句聯用時,過濾器做用於被統計的對象上。例如要統計書名以 "Django" 開頭的書:

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

上例先進行統計,而後在統計的結果上使用了過濾器。

annotate() 和 filter() 子句的順序

當使用同時包含 annotate() 和 filter() 子句的複雜查詢時,要特別當心兩種子句的順序。

當一個 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.0 )的出版商。可是,第一個查詢中的統計會提供出版商的全部書的數量;第二個查詢中的統計只返回好書的數量。第一個查詢中統計先於過濾器,因此過濾器對統計沒有做用。而第二個查詢過濾器先於統計,因此統計的對象是已通過濾過的。

order_by()

統計能夠做爲排序的基礎。當你定義一個 order_by 子句時,能夠引用 annotate() 子句中的統計。

例如,要依據書的做者人數進行排序,能夠這樣:

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

values()

一般,統計會針對 查詢集 中每個對象返回一個結果。可是,當使用 values 子句來約束要統計的列時,返回的結果會有所不一樣。原先統計結果中,統計字段的值相同的項會分組合並統計。

例如,要統計每一個做者各自所寫的書的平均評分:

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

返回的結果會包含每個做者及其所寫的書的平均計分。

可是,若是使用 values() 子句,返回的結果會有所不一樣:

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

這個例子中會把做者按名字分組統計,返回的結果中不會有重複的做者名字。名字相同的做者在統計中會做爲同一個做者來統計,同名做者所寫的書的評分會合併爲一個做者的書來統計。

annotate() 和 values() 子句的順序

當使用 filter() 子句時, annotate() 和 values() 子句的順序是很是重要的。若是 values() 子句先於 annotate() 子句,會按照前文所述的方式統計。

可是,若是 annotate() 子句先於 values() 子句,那麼統計會做用於整個查詢集,而 values() 子句只約束統計輸出的字段。

例如,若是咱們把前一個例子中的 values() 和 annotate() 子句調換順序:

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

這個例子會爲每個做者生成惟一的結果。可是在輸了的數據中只會包含做者名和 average_rating 的統計。

你能夠注意到 average_rating 在例子中顯示地定義了。在 annotate() 和 values() 子句的順序處於這種狀況是必須顯式定義。

若是 values() 子句先於 annotate() 子句,那麼任何統計會自動添加到輸出結果中。可是 values() 子句在 annotate() 子句以後,那麼必須顯式定義統計列。

缺省排序或 order_by() 子句的反作用

一個查詢集中 order_by() 子句中的字段(或一個模型中缺省排序字段)會對輸了數據產生影響,即便在 values() 中沒有這些字段的定義時也一樣會影響。這些特殊的字段會影響統計結果,這種狀況在計數統計時尤其明顯。

假設有一個這樣的模型:

class Item(models.Model): name = models.CharField(max_length=10) data = models.IntegerField() class Meta: ordering = ["name"]

這裏的重點是做爲缺省排序的 name 字段。若是你想要統計不重複的 data 值出現了多少次,你可能會使用以下語句:

# 警告:這個語句不徹底正確! Item.objects.values("data").annotate(Count("id"))

...這個語句看似會根據 data 值分組統計 Item 對象的 id 。但統計結果中 name 字段也會參與其中,因此這個語句實際的是不重複的 (data, name) 配對,而這不是咱們所要的結果,所以咱們應當這樣統計:

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

...這裏咱們經過一個空的「 order_by() 」 來清除反作用。

這個行爲與在查詢集文檔中 distinct() 提到的同樣。一般的規則是:當你不想要額外的字段在統計結果中產生做用時,必須清空排序的內容或者至少確認 values()子句中的字段已經限制了這些額外字段。

Note

你能夠會問爲何 Django 不去除這些字段的影響。主要的緣由是爲維護 distinct() 的一向性和一個原則: Django 從不 刪除你的排序定義(咱們不會改變那麼模型方法的行爲,不然就會違背咱們 API stability 策略)。

對小計進行統計

你能夠對小計的結果進行統計。在查詢中,你可使用 aggregate() 子句來對 annotate() 的結果進行統計。

例如,假設你要統計每本書的做者人數的平均值,那麼首先要計算每本書的做者人數,而後根據這個結果來統計平均值:

>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors')) {'num_authors__avg': 1.66}
轉自http://blog.csdn.net/shanliangliuxing/article/details/7927899
相關文章
相關標籤/搜索