Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

Django 中使用 annotate() 與 Avg() 進行平均評分計算

最後更新:2025年1月7日Python

本文為 留言評分功能 系列教學,第 15 篇:

  1. 使用 Django + Tailwind + Alpine.js 實作「五顆星評分」功能教學
  2. Django validators 驗證器完整教學
  3. Django PositiveSmallIntegerField 新手指南
  4. 在 esbuild 專案中整合 Alpine.js 的完整指南
  5. 使用 Alpine.js 建立星星評分表單 — 新手指南
  6. 深入理解 Alpine.js 中的 template 標籤使用指南
  7. Django 網站如何新增「星星評分」功能 — 後端接收邏輯
  8. Django 表單:如何讓使用者選擇性提交星星評分與留言
  9. Django 如何限制同一使用者只能對同一服務留言一次?
  10. Django 留言軟刪除邏輯|程式碼解析
  11. Django 計算評分摘要教學 — 使用 ORM 進行星等統計
  12. Python Django 使用 annotate、aggregate 統計教學
  13. 使用 Alpine.js 實作星級評分分佈 – 詳細教學
  14. Alpine.js 與 Tailwind CSS 動態樣式解析:為什麼有些樣式無法生效?
  15. Django 中使用 annotate() 與 Avg() 進行平均評分計算 👈 所在位置

在 Django 開發中,我們經常需要針對資料庫進行統計計算,例如:

  • 計算每個服務的平均評分
  • 統計每位用戶的總評論數

為了提高效能並減少不必要的查詢次數,Django 提供了強大的 ORM 聚合工具,如 annotate() 與 Avg(),可以讓我們直接在資料庫層完成統計計算。

本篇文章將詳細解釋如何使用 annotate() 和 Avg() 來計算 服務的平均評分,並透過範例與數據圖解幫助新手理解。


什麼是 annotate()?

定義:

annotate() 是 Django ORM 中的註解方法,用來為查詢集 (QuerySet) 附加額外的計算結果。
這些計算結果可以是:

  • 平均值 (Avg)
  • 計數 (Count)
  • 總和 (Sum)
  • 最大值 (Max)
  • 最小值 (Min)

如何使用 annotate()?

範例:計算每個服務的平均評分

from django.db.models import Avg
from .models import Service

services = Service.objects.annotate(average_rating=Avg('comments__rating'))

這段程式碼的作用:

  1. Service.objects.annotate(...)
    • 查詢所有 Service (服務) 物件
  2. average_rating=Avg('comments__rating')
    • 使用 Avg 函數計算每個服務的所有 Comment (評論) 中的 rating 平均值
  3. 結果:
    • 每個 Service 物件會自動附加一個 average_rating 欄位

資料範例

服務 (Service) 表格

IDTitleFreelancer
1Logo DesignAlice
2Web DesignBob
TitleLogo Design
FreelancerAlice
TitleWeb Design
FreelancerBob

評論 (Comment) 表格

IDService IDRatingUser
115UserA
214UserB
323UserC
Service ID1
Rating5
UserUserA
Service ID1
Rating4
UserUserB
Service ID2
Rating3
UserUserC

查詢結果 (使用 annotate() 後)

services = Service.objects.annotate(average_rating=Avg('comments__rating'))
Service TitleFreelancerAverage Rating
Logo DesignAlice4.5
Web DesignBob3.0
FreelancerAlice
Average Rating4.5
FreelancerBob
Average Rating3.0

annotate() 的三大核心步驟

使用 annotate() 時,Django 其實做了三件事:

  • 🎯 分組 (Grouping):將資料按照服務 (Service) 進行分組。
  • 🎯 計算 (Calculation):針對每組數據 (同一個服務的所有評論),計算 comments__rating 的平均分數 (Avg)。
  • 🎯 產生新欄位 (Add New Field):將計算結果 (average_rating) 附加 到查詢結果中作為新的欄位。

使用 SQL 解釋 annotate() 背後的原理

其實,annotate() 在資料庫底層會自動生成 SQL 查詢:

SELECT 
    service.id, 
    service.title, 
    AVG(comment.rating) AS average_rating
FROM 
    service
LEFT JOIN 
    comment ON service.id = comment.service_id
GROUP BY 
    service.id;
  1. SELECT:選擇需要的欄位 (服務 ID、標題與平均分數)。
  2. FROM:設定查詢的主表 (service)。
  3. LEFT JOIN:連接 comment 表,保留所有服務資料,即使沒有評論。
  4. GROUP BY:根據服務分組,每個服務計算一次。
  5. AVG:計算每個服務的平均分數。

為什麼使用 annotate() 是更好的做法?

效能更佳

  • 在 資料庫層 完成計算 (SQL 層計算),減少了 Python 端的重複查詢與計算 (N+1 查詢問題)。

可擴展性更高

  • 可以輕鬆擴展計算其他統計數據,如 Count、Sum、Max 等。

更簡潔的程式碼

  • 對比傳統的 Python 迴圈計算,程式碼更簡潔且容易維護。

範例:在 freelancer_dashboard 中使用 annotate()

views.py

from django.db.models import Avg
from django.shortcuts import render
from .models import Service

def freelancer_dashboard(request, id):
    freelancer = request.user

    # 使用 annotate 計算每個服務的平均評分
    services = (
        Service.objects.filter(freelancer_user=freelancer)
        .annotate(average_rating=Avg('comments__rating'))
        .order_by("-created_at")
    )

    return render(request, "services/freelancer_dashboard.html", {"services": services})

前端範例 (freelancer_dashboard.html)

{% for service in services %}
    <div class="border p-4 rounded-lg shadow mb-4">
        <h3 class="text-xl font-bold">{{ service.title }}</h3>
        <p class="text-gray-600">{{ service.description }}</p>

        <!-- ⭐ 顯示平均評分 -->
        <p class="text-yellow-500 text-lg">
            平均評分: {{ service.average_rating|floatformat:1 }} ★
        </p>

        <!-- ✅ 無評分時的處理 -->
        {% if service.average_rating is None %}
            <p class="text-gray-500">尚無評分</p>
        {% endif %}
    </div>
{% endfor %}

總結

  1. annotate() 是 Django ORM 中的註解工具,用於對查詢結果附加統計數據。
  2. 在計算平均分數時,使用 Avg() 可以直接在資料庫層完成計算,效能更佳。
  3. 推薦做法:在資料庫端進行計算,避免 Python 端重複查詢 (N+1 問題)。

這樣的解釋清楚了嗎?😊 如果還有疑問,隨時問我!

目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

Python

目錄

  • 什麼是 annotate()?
  • 定義:
  • 如何使用 annotate()?
  • 資料範例
  • 查詢結果 (使用 annotate() 後)
  • annotate() 的三大核心步驟
  • 使用 SQL 解釋 annotate() 背後的原理
  • 為什麼使用 annotate() 是更好的做法?
  • 範例:在 freelancer_dashboard 中使用 annotate()
  • 總結