使用訊息框架提供操作回饋¶
開始之前¶
任務目標
在這個章節中,我們會完成:
- 了解 Django Messages Framework
- 在建立文章後顯示成功訊息
- 在編輯文章後顯示成功訊息
- 在刪除文章後顯示成功訊息
- 使用 django-bootstrap5 美化訊息樣式
為什麼需要訊息回饋?¶
目前我們的網站在執行操作後,使用者可能不確定操作是否成功:
使用者建立文章 → 重導向到文章詳情頁
→ 使用者心想:「文章真的建立成功了嗎?」
使用者編輯文章 → 重導向到文章詳情頁
→ 使用者心想:「修改真的儲存了嗎?」
使用者刪除文章 → 重導向到文章列表
→ 使用者心想:「文章真的被刪除了嗎?」
缺少回饋的問題
- 使用者不確定操作是否成功
- 可能重複提交表單(以為沒成功,再送一次)
- 使用者體驗不佳
好的使用者介面應該:
- ✅ 明確告知操作結果
- ✅ 提供視覺回饋
- ✅ 讓使用者安心
Django Messages Framework¶
Django 內建的 Messages Framework 可以輕鬆實作「一次性訊息」:
特色¶
- 自動清除:訊息顯示一次後自動消失
- 支援不同等級:成功、錯誤、警告、資訊
- 跨頁面顯示:使用 Cookie 或 Session 儲存,可以在重導向後顯示
- 整合簡單:只需要幾行程式碼
訊息等級¶
| 等級 | 函式 | 用途 | Bootstrap 樣式 |
|---|---|---|---|
| DEBUG | messages.debug() |
開發時的除錯訊息 | alert-warning |
| INFO | messages.info() |
一般資訊 | alert-info |
| SUCCESS | messages.success() |
操作成功 | alert-success |
| WARNING | messages.warning() |
警告訊息 | alert-warning |
| ERROR | messages.error() |
錯誤訊息 | alert-danger |
訊息等級對應
Django Messages Framework 的等級名稱會自動對應到 Bootstrap 的 alert 樣式:
success→alert-success(綠色)error→alert-danger(紅色)warning→alert-warning(黃色)info→alert-info(藍色)
這讓我們可以輕鬆整合 Bootstrap 的樣式!
在 Views 加入訊息¶
讓我們在建立、編輯、刪除操作後加入訊息。
修改 Views¶
修改 blog/views.py:
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from blog.forms import ArticleForm
from blog.models import Article
def article_list(request):
articles = Article.objects.select_related("author").prefetch_related("tags")
return render(request, "blog/article_list.html", {"articles": articles})
def article_detail(request, article_id):
article = get_object_or_404(
Article.objects.select_related("author").prefetch_related("tags"),
id=article_id,
)
return render(request, "blog/article_detail.html", {"article": article})
def article_create(request):
form = ArticleForm(request.POST or None)
if form.is_valid():
article = form.save()
messages.success(request, f"文章「{article.title}」已成功建立。") # (1)!
return redirect("blog:article_detail", article_id=article.id)
return render(request, "blog/article_create.html", {"form": form})
def article_edit(request, article_id):
article = get_object_or_404(Article, id=article_id)
form = ArticleForm(request.POST or None, instance=article)
if form.is_valid():
article = form.save()
messages.success(request, f"文章「{article.title}」已成功更新。") # (2)!
return redirect("blog:article_detail", article_id=article.id)
return render(request, "blog/article_edit.html", {"form": form, "article": article})
def article_delete(request, article_id):
article = get_object_or_404(Article, id=article_id)
if request.method == "POST":
article.delete()
messages.success(request, f"文章「{article.title}」已成功刪除。") # (3)!
return redirect("blog:article_list")
return render(request, "blog/article_delete.html", {"article": article})
- 建立文章後加入成功訊息
- 編輯文章後加入成功訊息
- 刪除文章後加入成功訊息
程式碼重點
- 匯入 messages 模組:
from django.contrib import messages - 加入訊息:
messages.success(request, "訊息內容") - 在 redirect 前加入:訊息會儲存起來(Cookie 或 Session),重導向後依然可以顯示
- 使用 f-string:動態顯示文章標題
在 Template 顯示訊息¶
使用 django-bootstrap5¶
django-bootstrap5 套件提供了方便的 template tag 來顯示訊息。
修改 templates/base.html:
{% load django_bootstrap5 %}
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Django Playground" />
<meta name="keywords" content="Django, Playground" />
<title>
{% block title %}
Django 大冒險
{% endblock title %}
</title>
{% bootstrap_css %}
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" />
{% block extra_head %}
{% endblock extra_head %}
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<span class="navbar-brand">Django 大冒險</span>
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'blog:article_list' %}">文章列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'admin:index' %}">管理後台</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="container my-4">
{% bootstrap_messages %}
{% block content %}
{% endblock content %}
</main>
<footer class="bg-light py-4 mt-5">
<div class="container text-center">
<p class="text-muted mb-0">
Django Playground
</p>
</div>
</footer>
{% bootstrap_javascript %}
{% block extra_scripts %}
{% endblock extra_scripts %}
</body>
</html>
bootstrap_messages 的功能
{% bootstrap_messages %} 會自動:
- 迭代所有訊息:顯示所有待顯示的訊息
- 套用正確的 Bootstrap 樣式:根據訊息等級自動使用對應的 alert 樣式
- 加入關閉按鈕:使用者可以手動關閉訊息
- 加入動畫效果:使用 Bootstrap 的 fade 效果
這比手動寫 template 程式碼簡單多了!
手動寫法(參考)¶
如果想要更客製化,也可以手動寫:
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show"
role="alert">
{{ message }}
<button type="button"
class="btn-close"
data-bs-dismiss="alert"
aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
兩種寫法比較
| 項目 | {% bootstrap_messages %} |
手動寫法 |
|---|---|---|
| 程式碼量 | 1 行 | 10+ 行 |
| 樣式 | 自動套用 Bootstrap | 需要手動設定 |
| 客製化 | 較少 | 完全自由 |
| 維護性 | 簡單 | 較複雜 |
大多數情況下,使用 {% bootstrap_messages %} 就足夠了!
測試訊息功能¶
啟動開發伺服器:
測試建立文章¶
- 訪問 http://127.0.0.1:8000/blog/articles/create/
- 填寫表單並送出
- 重導向到文章詳情頁
- 看到綠色的成功訊息:「文章「{標題}」已成功建立。」
測試編輯文章¶
- 在文章詳情頁點擊「編輯」
- 修改內容並儲存
- 重導向到文章詳情頁
- 看到綠色的成功訊息:「文章「{標題}」已成功更新。」
測試刪除文章¶
- 在文章詳情頁點擊「刪除」
- 在確認頁面點擊「確認刪除」
- 重導向到文章列表頁
- 看到綠色的成功訊息:「文章「{標題}」已成功刪除。」
測試關閉訊息¶
- 點擊訊息右上角的 ✕ 按鈕
- 訊息消失
測試自動清除¶
- 看到訊息後,重新整理頁面
- 訊息不再出現(已自動清除)
訊息功能運作正常
現在使用者可以:
- ✅ 明確知道操作是否成功
- ✅ 看到綠色的成功提示(視覺回饋)
- ✅ 手動關閉訊息
- ✅ 訊息不會一直出現(自動清除)
進階:客製化訊息顯示¶
修改訊息位置¶
如果想讓訊息顯示在特定位置,可以移動 {% bootstrap_messages %}:
<body>
<div class="container my-4">
<h1 class="mb-4">Django 大冒險</h1>
{% block blog_content %}{% endblock blog_content %}
<!-- 訊息顯示在底部 -->
{% bootstrap_messages %}
</div>
</body>
使用不同的訊息等級¶
在不同情況下使用不同的訊息等級:
# 成功訊息(綠色)
messages.success(request, "操作成功!")
# 資訊訊息(藍色)
messages.info(request, "這是一個提示訊息。")
# 警告訊息(黃色)
messages.warning(request, "請注意這個操作。")
# 錯誤訊息(紅色)
messages.error(request, "發生錯誤!")
實際應用範例¶
修改 article_create view,加入驗證失敗的訊息:
def article_create(request):
form = ArticleForm(request.POST or None)
if form.is_valid():
article = form.save()
messages.success(request, f"文章「{article.title}」已成功建立。")
return redirect("blog:article_detail", article_id=article.id)
# 表單驗證失敗
if request.method == "POST":
messages.error(request, "表單填寫有誤,請檢查並重新送出。")
return render(request, "blog/article_create.html", {"form": form})
何時使用錯誤訊息
一般來說,Django Form 的驗證錯誤會直接顯示在表單欄位旁,不需要額外加入錯誤訊息。
只有在以下情況才需要加入錯誤訊息:
- 非表單驗證的錯誤(例如:權限不足)
- 需要在頁面頂部顯示總體錯誤
- 自訂的業務邏輯錯誤
大多數情況下,使用成功訊息就足夠了!
Messages Framework 運作原理¶
讓我們了解 Messages Framework 如何運作。
儲存機制¶
Django Messages Framework 預設使用 FallbackStorage,它會智慧地選擇儲存方式:
1. 在 View 中呼叫 messages.success()
→ 訊息先嘗試儲存在 Cookie 中
2. 如果訊息太大(超過 2048 bytes)
→ 自動改用 Session 儲存
3. 重導向到其他頁面
→ Cookie 或 Session 資料跟著使用者移動
4. Template 顯示訊息
→ 從 Cookie 或 Session 讀取訊息並顯示
5. 訊息顯示後
→ 自動清除訊息
為什麼使用 Cookie 優先?
- 效能更好:不需要在伺服器端儲存資料
- 減少 Session 寫入:只有大訊息才會用到 Session
- 自動降級:訊息太大時自動切換到 Session
這就是 FallbackStorage 的設計精髓!
為什麼可以跨頁面?¶
sequenceDiagram
participant View as article_create View
participant Storage as FallbackStorage
participant Browser as 瀏覽器
participant Template as article_detail Template
View->>Storage: 儲存訊息
Note over Storage: 先嘗試 Cookie<br/>太大則用 Session
View->>Browser: redirect("article_detail")
Note over Browser: Cookie 跟著請求傳送
Browser->>Template: GET /articles/1/
Template->>Storage: 讀取訊息
Storage-->>Template: ["文章已建立"]
Template->>Browser: 顯示訊息
Template->>Storage: 清除訊息
FallbackStorage 的優勢
使用 FallbackStorage 儲存訊息的好處:
- 可以跨頁面:重導向後依然可以顯示
- 自動清除:顯示一次後自動刪除
- 使用者隔離:每個使用者看到自己的訊息
- 不影響 URL:訊息不會出現在網址列
- 效能優化:優先使用 Cookie,減少 Session 負擔
這就是為什麼 Messages Framework 如此好用!
常見問題¶
為什麼訊息沒有顯示?¶
檢查以下幾點:
-
是否載入 django_bootstrap5:
-
是否加入 bootstrap_messages:
-
是否在正確的時機加入訊息:
-
是否載入 Bootstrap JavaScript:
關閉按鈕需要 Bootstrap 的 JavaScript。
訊息會一直顯示嗎?¶
不會!訊息是「一次性」的:
- 第一次訪問頁面:顯示訊息
- 重新整理頁面:訊息消失
如果訊息一直出現,可能是:
- 每次訪問頁面都執行了
messages.success() - 檢查 View 的邏輯
可以一次顯示多個訊息嗎?¶
可以!Messages Framework 會自動處理:
任務結束¶
完成!
恭喜你完成了這個章節!現在你已經:
- 了解 Django Messages Framework
- 在建立文章後顯示成功訊息
- 在編輯文章後顯示成功訊息
- 在刪除文章後顯示成功訊息
- 使用 django-bootstrap5 美化訊息樣式
你學會了:
- 使用者體驗的重要性:明確的操作回饋讓使用者更安心
- Messages Framework 的運作原理:使用 FallbackStorage(Cookie + Session)實作跨頁面訊息
- 整合 django-bootstrap5:一行程式碼搞定訊息顯示
- 不同的訊息等級:成功、錯誤、警告、資訊