Python Django 使用 annotate、aggregate 統計教學
更新日期: 2025 年 1 月 7 日
本文為 留言評分功能 系列教學,第 12 篇:
- 使用 Django + Tailwind + Alpine.js 實作「五顆星評分」功能教學
- Django validators 驗證器完整教學
- Django PositiveSmallIntegerField 新手指南
- 在 esbuild 專案中整合 Alpine.js 的完整指南
- 使用 Alpine.js 建立星星評分表單 — 新手指南
- 深入理解 Alpine.js 中的 template 標籤使用指南
- Django 網站如何新增「星星評分」功能 — 後端接收邏輯
- Django 表單:如何讓使用者選擇性提交星星評分與留言
- Django 如何限制同一使用者只能對同一服務留言一次?
- Django 留言軟刪除邏輯|程式碼解析
- Django 計算評分摘要教學 — 使用 ORM 進行星等統計
- Python Django 使用 annotate、aggregate 統計教學 👈 所在位置
- 使用 Alpine.js 實作星級評分分佈 – 詳細教學
- Alpine.js 與 Tailwind CSS 動態樣式解析:為什麼有些樣式無法生效?
- Django 中使用 annotate() 與 Avg() 進行平均評分計算
在 Django 專案中,經常需要對使用者的評論資料進行統計分析,例如計算各星等的分佈、總評論數以及平均評分等數據。
這篇文章將詳細解析如何透過 Django ORM 進行這些計算,並搭配圖表與範例數據,讓新手能夠更直觀地理解整個流程。
comments
是什麼?
comments = Comment.objects.filter(service=service, is_deleted=False)
解釋
comments
是一個 QuerySet 物件,代表從資料庫中查詢出來的所有符合條件的留言記錄。- 這段程式碼篩選了 與特定服務 (
service
) 相關,且 未被軟刪除 (is_deleted=False
) 的評論資料。
範例數據
+----+---------+--------+
| ID | Rating | User |
+----+---------+--------+
| 1 | 5 | Alice |
| 2 | 4 | Bob |
| 3 | 5 | Carol |
| 4 | 3 | David |
| 5 | 5 | Eve |
| 6 | 4 | Frank |
| 7 | 5 | Grace |
+----+---------+--------+
使用 .values('rating')
回傳指定欄位
comments.values('rating')
解釋
.values('rating')
會將rating
欄位的數據轉換成 字典格式,方便後續進行統計計算。
範例輸出
[
{'rating': 5},
{'rating': 4},
{'rating': 5},
{'rating': 3},
{'rating': 5},
{'rating': 4},
{'rating': 5}
]
使用 annotate(Count('rating'))
分組統計
from django.db.models import Count
rating_summary = comments.values('rating').annotate(count=Count('rating'))
使用 .annotate()
時,Django 會一次幫你完成以下 三件事:
👉 分組 (Grouping):把相同的數據分類成一組
- 使用
.values('欄位名稱')
來告訴 Django 依據哪些欄位來分組。 - 例如:
values('rating')
會根據rating
欄位將相同星等的評論分成一組。
👉 計算 (Calculation):針對每組數據計算出一個數值
- 透過
Count()
、Sum()
、Avg()
等 Django 提供的聚合函數來計算統計數據。 - 例如:
Count('rating')
→ 計算每個星等的數量Avg('rating')
→ 計算每組的平均評分Sum('rating')
→ 計算每組的總分
👉 新增欄位 (Adding a Calculated Field):把計算結果放到一個新的欄位中
計算結果會自動成為查詢結果中的新欄位,名稱由你指定 (例如:count
、total
、average
等)。
.annotate()
與 Count()
的實作步驟解析
以下以計算評論的星等分佈為例,逐步拆解這段程式碼的工作原理:
rating_summary = Comment.objects.values('rating').annotate(count=Count('rating'))
取得原始資料
+----+---------+--------+
| ID | Rating | User |
+----+---------+--------+
| 1 | 5 | Alice |
| 2 | 4 | Bob |
| 3 | 5 | Carol |
| 4 | 3 | David |
| 5 | 5 | Eve |
| 6 | 4 | Frank |
| 7 | 5 | Grace |
+----+---------+--------+
.values('rating')
分組
使用 .values('rating')
將 rating
欄位的資料分組:
[
{'rating': 5},
{'rating': 4},
{'rating': 5},
{'rating': 3},
{'rating': 5},
{'rating': 4},
{'rating': 5}
]
.annotate(Count('rating'))
統計數量
針對每個分組計算 rating
的數量:
+---------+-------+
| Rating | Count |
+---------+-------+
| 5 | 4 |
| 4 | 2 |
| 3 | 1 |
+---------+-------+
儲存到 rating_summary
結果儲存在變數 rating_summary
中,以便進一步使用。
rating_summary = [
{'rating': 5, 'count': 4},
{'rating': 4, 'count': 2},
{'rating': 3, 'count': 1}
]
.annotate()
就像在 Excel 裡建立樞紐表
原始 Excel 表格:
學生 | 分數 |
---|---|
Alice | 90 |
Bob | 80 |
Carol | 90 |
David | 70 |
Eve | 80 |
Frank | 90 |
Grace | 90 |
使用 Excel 樞紐表 (Django .annotate()
做的事情):
分數 | 人數 |
---|---|
90 | 4 |
80 | 2 |
70 | 1 |
使用 count()
計算總評論數
total_reviews = comments.count()
解釋
.count()
直接計算查詢集中的總筆數。
範例輸出
總評論數: 7
使用 aggregate(Avg('rating'))
計算平均評分
from django.db.models import Avg
average_rating = comments.aggregate(Avg('rating'))['rating__avg']
解釋
.aggregate()
是 Django ORM 中用來計算整個查詢集的總體統計數據的函數。
.aggregate()
用於計算整個查詢集的總體統計數據。Avg('rating')
計算rating
欄位的平均分數。
它與 .annotate()
類似,但主要用於整體統計,而不是針對每個分組分別計算。
簡單來說,.aggregate()
主要做兩件事:
- 計算:對整個查詢集的所有數據執行計算 (如平均數、總和、最大值等)。
- 回傳結果:將計算結果以 字典 形式回傳。
補充:
['rating__avg']
是什麼?
- 當你使用
aggregate(Avg('rating'))
時,Django 會自動在結果字典中使用這個命名規則:
rating
➡️ 你查詢的欄位__avg
➡️ Django 內建的Avg()
聚合函數自動附加的後綴範例
average_rating = comments.aggregate(Avg('rating')) print(average_rating) # {'rating__avg': 4.5}
因為
aggregate()
回傳的是一個字典,所以你需要透過['rating__avg']
取得計算結果。
計算過程
rating__avg = (5 + 4 + 5 + 3 + 5 + 4 + 5) / 7
rating__avg = 4.43
回傳值
{'rating__avg': 4.43}
建立完整的星等分佈 (1-5星)
# 建立 1-5 星的分佈
rating_distribution = {i: 0 for i in range(1, 6)}
# 將統計結果填入分佈字典
for entry in rating_summary:
rating_distribution[entry['rating']] = entry['count']
範例輸出
+---------+-------+
| Rating | Count |
+---------+-------+
| 5 | 4 |
| 4 | 2 |
| 3 | 1 |
| 2 | 0 |
| 1 | 0 |
+---------+-------+
解釋
- 建立一個包含
1
到5
星的字典,並將計算結果填入。 - 如果某些星等沒有出現,預設為
0
。
完整流程示意圖
Step 1: 原始資料 (comments)
+----+---------+--------+
| ID | Rating | User |
+----+---------+--------+
| 1 | 5 | Alice |
| 2 | 4 | Bob |
| 3 | 5 | Carol |
| 4 | 3 | David |
| 5 | 5 | Eve |
| 6 | 4 | Frank |
| 7 | 5 | Grace |
+----+---------+--------+
Step 2: `.values('rating')`
[{'rating': 5}, {'rating': 4}, {'rating': 5}, {'rating': 3}, ...]
Step 3: `.annotate(Count('rating'))`
+---------+-------+
| Rating | Count |
+---------+-------+
| 5 | 4 |
| 4 | 2 |
| 3 | 1 |
+---------+-------+
Step 4: `.aggregate(Avg('rating'))`
average_rating = (5 + 4 + 5 + 3 + 5 + 4 + 5) / 7 = **4.43**
Step 5: `rating_distribution`
+---------+-------+
| Rating | Count |
+---------+-------+
| 5 | 4 |
| 4 | 2 |
| 3 | 1 |
| 2 | 0 |
| 1 | 0 |
+---------+-------+
總結
這段程式碼透過 Django ORM 完成了以下步驟:
- 使用
filter()
篩選出符合條件的評論資料。 - 使用
.values()
將資料轉換為字典格式,方便進一步操作。 - 使用
.annotate()
與Count()
計算各星等的評論數量。 - 使用
.count()
取得總評論數。 - 使用
.aggregate(Avg())
計算平均評分。 - 建立完整的星等分佈 並確保 1 到 5 星都正確顯示,即使某些星等為 0。
希望這篇教學有幫助到你,讓你更理解如何使用 Django ORM 進行評論統計計算!