Django之資料聚合函式 annotate

2021-08-23 14:43:47 字數 3685 閱讀 3797

在我們的部落格側邊欄有分類列表,顯示部落格已有的全部文章分類。現在想在分類名後顯示該分類下有多少篇文章,該怎麼做呢?最優雅的方式就是使用 django 模型管理器的annotate方法。

blog/models.py

class

article(models.model):

title = models.charfield(max_length=70)

body =models.textfield()

category = models.foreignkey('

category')

#其它屬性...

def__str__

(self):

return

self.title

class

category(models.model):

name = models.charfield(max_length=100)

我們知道從資料庫取資料都是使用模型管理器 objects 的方法實現的。比如獲取全部分類是:category.objects.all() ,假設有乙個名為 test 的分類,那麼獲取該分類的方法是:category.objects.get(name='text') 。objects 除了 all , get 等方法外,還有很多運算元據庫的方法,而其中有乙個 annotate 方法,該方法正可以幫我們實現本文所關注的統計分類下的文章數量的功能。

annotate 方法在底層呼叫了資料庫的資料聚合函式,下面使用乙個實際的資料庫表來幫助我們理解 annotate 方法的工作原理。在 article 模型中我們通過 foreignkey 把 article和 category 關聯了起來,這時候它們的資料庫表結構就像下面這樣:

article 表:

idtitle

body

category_id

1article 1

...1

2article 2

...1

3article 3

...1

4article 4

...2

category 表:

name

idcategory 1

1category 22

這裡前 3 篇文章屬於 category 1,第 4 篇文章屬於 category 2。

當 django 要查詢某篇 article 對應的分類時,比如 article 1,首先查詢到它分類的 id 為 1,然後 django 再去 category 表找到 id 為 1 的那一行,這一行就是 article 1 對應的分類。反過來,如果要查詢 category 1 對應的全部文章呢?category 1 在 category 表中對應的 id 是 1,django 就在 article 表中搜尋哪些行的 category_id 為 1,發現前 3 行都是,把這些行取出來就是 category 1 下的全部文章了。同理,這裡 annotate 做的事情就是把全部 category 取出來,然後去 article 查詢每乙個 category 對應的文章,查詢完成後只需算一下每個 category id 對應有多少行記錄,這樣就可以統計出每個 category 下有多少篇文章了。把這個統計數字儲存到每一條 category 的記錄就可以了(當然並非儲存到資料庫,在 django orm 中是儲存到 category 的例項的屬性中,每個例項對應一條記錄)。

以上是原理方面的分析,具體到 django 中該如何用呢?在我們的部落格中,獲取側邊欄的分類列表的方法寫在模板標籤 get_categories 裡,因此我們修改一下這個函式,具體**如下:

blog/templatetags/blog_tags.py

from django.db.models.aggregates import

count

from blog.models import

category

@register.******_tag

defget_categories():

#記得在頂部引入 count 函式

#count 計算分類下的文章數,其接受的引數為需要計數的模型的名稱

return category.objects.annotate(num_articles=count('

article

')).filter(num_articles__gt=0)

這個 category.objects.annotate 方法和category.objects.all 有點類似,它會返回資料庫中全部 category 的記錄,但同時它還會做一些額外的事情,在這裡我們希望它做的額外事情就是去統計返回的 category 記錄的集合中每條記錄下的文章數。**中的 count 方法為我們做了這個事,它接收乙個和 categoty 相關聯的模型引數名(這裡是 article,通過 foreignkey 關聯的),然後它便會統計 category 記錄的集合中每條記錄下的與之關聯的 post 記錄的行數,也就是文章數,最後把這個值儲存到 num_articles 屬性中。

此外,我們還對結果集做了乙個過濾,使用 filter 方法把 num_articles 的值小於 1 的分類過濾掉。因為 num_articles 的值小於 1 表示該分類下沒有文章,沒有文章的分類我們不希望它在頁面中顯示。關於 filter 函式以及查詢表示式(雙下劃線)在之前已經講過,具體請參考分類與歸檔。

現在在 category 列表中每一項都新增了乙個  num_articles 屬性記錄該 category 下的文章數量,我們就可以在模板中引用這個屬性來顯示分類下的文章數量了。

templates/base.html

也就是在模板中通過模板變數 } 顯示  num_articles 的值。開啟開發伺服器,可以看到分類名後正確地顯示了該分類下的文章數了,而沒有文章分類則不會在分類列表中出現。

此外,annotate 方法不侷限於用於本文提到的統計分類下的文章數,你也可以舉一反三,只要是兩個 model 類通過 foreignkey 或者 manytomany 關聯起來,那麼就可以使用 annotate 方法來統計數量。比如下面這樣乙個標籤系統:

class

article(models.model):

title = models.charfield(max_length=70)

body =models.textfield()

tags = models.manytomany('

tag'

)

def__str__

(self):

return

self.title

class

tag(models.model):

name = models.charfield(max_length=100)

from django.db.models.aggregates import

count

from blog.models import

tag#

count 計算分類下的文章數,其接受的引數為需要計數的模型的名稱

tag_list = tag.objects.annotate(num_posts=count('

post

'))

關於 annotate 方法官方文件的說明在這裡:annotate。同時也建議了解了解 objects 下的其它運算元據庫的方法,以便在遇到相關問題時知道去**查閱。

17 資料統計之聚合函式使用

grouped.agg np.mean,np.max head 20 grouped.agg loan info dkje ye y e agg np.sum,np.mean 按weekday欄位進行分組 grouped df.grouped weekday 只能對乙個方法進行統計處理 groupe...

SQL 聚合函式 非聚合函式

聚合函式 聚合函式就是對一組值進行計算後返回單個值 即分組 聚合函式在計算時都會忽略空值 null 所有的聚合函式均為確定性函式。即任何時候使用一組相同的輸入值呼叫聚合函式執行後的返回值都是相同的,無二義性。2 聚合開窗函式 聚合函式加上 over 開窗函式就是聚合開窗函式。create table...

資料傾斜方案之聚合源資料

效能調優,最有效,最直接,最簡單的方式,就是加資源,加並行度,注意rdd架構 復用同乙個rdd,加上cache快取 shuffle,jvm等,是次要的。資料傾斜,解決方案 第乙個方案 聚合源資料 現在,做一些聚合操作,groupbykey,reducebykey這種操作,說白了,就是拿到每個key對...