Django 留言功能新增與視圖互動詳解

更新日期: 2024 年 12 月 2 日

在建立完整的留言功能時,不僅需要處理頁面顯示與表單提交,還需要理解 Django 應用之間如何協作。

本指南將帶你完成留言功能的設置與優化,並詳細解釋應用間的互動原理。


調整 Resumes 應用的顯示頁面

resumes 應用的 show.html 文件中,我們需要新增留言表單,讓用戶可以針對履歷新增留言。

目錄結構

mysite/
├── resumes/
   ├── templates/
       ├── resumes/
           ├── show.html

新增留言表單

{% extends "shared/layout.html" %} {% block content %} {% if messages %}
<div class="alert-box">
  {% for message in messages %}
  <div class="alert {{ message.tags }}">{{ message }}</div>
  {% endfor %}
</div>
{% endif %}

<h1>show! {{ resume.title }}</h1>
<h3>{{ resume.skill }}</h3>

<article>
  <p>{{ resume.location }}</p>
  <p>{{ resume.content }}</p>
</article>
<footer>
  <a href="{% url 'resumes:edit' resume.id %}">編輯</a>
  <a href="{% url 'resumes:delete' resume.id %}">刪除</a>
</footer>

<section>
  <form action="{% url 'resumes:comments' resume.id %}" method="post">
    {% csrf_token %}
    <textarea name="content" id=""></textarea>
    <button>新增留言</button>
  </form>
</section>

新增留言表單功能詳解

在新增留言功能的表單中,我們對頁面的兩個區塊進行重點講解,分別是消息提示區塊與留言提交表單的處理邏輯。

消息提示區塊

{% if messages %}
<div class="alert-box">
  {% for message in messages %}
  <div class="alert {{ message.tags }}">{{ message }}</div>
  {% endfor %}
</div>
{% endif %}

區塊功能

  1. 檢查消息是否存在
    • {% if messages %}:確認是否有消息需要顯示。
    • messages 是 Django 中的內建消息框架,主要用於在視圖中傳遞用戶提示消息。
  2. 迴圈渲染消息
    • {% for message in messages %}:遍歷所有消息,將其逐一顯示在頁面中。
  3. 渲染樣式
    • <div class="alert {{ message.tags }}">:通過 message.tags 為消息添加動態的 CSS 樣式類別(如 successerror 等)。
    • message.tags 通常是視圖中傳遞的消息類型(如 successwarning),用於設定消息的樣式。

2. 留言提交表單區塊

<section>
  <form action="{% url 'resumes:comments' resume.id %}" method="post">
    {% csrf_token %}
    <textarea name="content" id=""></textarea>
    <button>新增留言</button>
  </form>
</section>

區塊功能

  1. 表單提交路徑
    • resume.id:將當前履歷的 ID 作為參數傳遞,確保留言正確關聯至對應的履歷。
    • <form action="{% url 'resumes:comments' resume.id %}" method="post">
      • {% url 'resumes:comments' resume.id %}
        • 生成動態路徑,例如:http://localhost:8000/resumes/1/comments
        • 對應於 resumes 應用的路由設定:
path("<int:id>/comments", comment_views.index, name="comments")
  1. CSRF 安全保護
    • {% csrf_token %}:插入跨站請求保護令牌,防止表單被惡意提交。
    • Django 的中間件會檢查該令牌,只有匹配的請求才能被接受。
  2. 留言內容輸入
    • <textarea name="content" id=""></textarea>
      • 提供用戶輸入留言內容的區域。
      • name="content":對應於提交時的鍵值,後端可通過 request.POST.get('content') 獲取內容。
  3. 提交按鈕
    • <button>新增留言</button>
      • 提交表單內容,觸發 POST 請求。

配置 Resumes 應用的路由

resumes 應用的 urls.py 文件中,新增留言處理的路由。

from django.urls import path
from . import views
from comments import views as comment_views  # 引入 comments 應用的視圖

app_name = 'resumes'

urlpatterns = [
    path("", views.home, name='list'),
    path("new/", views.new, name='nn'),
    path("<int:id>", views.show, name="show"),
    path("<int:id>/edit", views.edit, name="edit"),
    path("<int:id>/delete", views.delete, name="delete"),
    path("<int:id>/comments", comment_views.index, name="comments"),  # 新增留言路徑
]

解釋

  • comment_views.index
    • 來自 comments 應用的視圖函數,負責處理留言的新增。
  • <int:id>
    • 透過路徑參數傳遞履歷的 ID,讓視圖知道留言屬於哪份履歷。

編寫 Comments 應用的視圖

comments 應用的 views.py 中,新增留言處理邏輯:

from django.shortcuts import render, get_object_or_404, redirect
from django.views.decorators.http import require_POST
from django.contrib import messages
from resumes.models import Resume
from .models import Comment

@require_POST
def index(request, id):
    # 找出對應的 Resume
    resume = get_object_or_404(Resume, id=id)
    
    # 新增 Comment
    comment = Comment()
    comment.content = request.POST.get('content')  # 取得表單中的留言內容
    comment.resume_id = resume.id
    comment.save()

    # 顯示成功訊息
    messages.success(request, "新增留言成功!")

    # 轉址回原頁面
    return redirect('resumes:show', id=resume.id)

根據 ID 檢索履歷

resume = get_object_or_404(Resume, id=id)
  • 作用
    1. 從資料庫中查找 ID 為 id 的履歷。
    2. 如果找不到對應的履歷,返回 HTTP 404 錯誤頁面。
  • 為什麼使用 get_object_or_404
    • 確保代碼簡潔:相比於手動檢查是否找到對象並拋出錯誤,這是一種更簡潔的寫法。
    • 增強用戶體驗:當目標履歷不存在時,直接返回標準的 404 錯誤頁面,避免程序崩潰。

創建並保存留言

以下代碼負責創建並保存留言的核心邏輯:

comment = Comment()
comment.content = request.POST.get('content')  # 取得表單中的留言內容
comment.resume_id = resume.id  # 將留言與履歷關聯
comment.save()  # 保存留言至資料庫

2.1 創建 Comment 實例

comment = Comment()
  • 作用:初始化一個 Comment 模型的空白實例,為即將保存的留言準備容器。
  • 背後邏輯
    • Django 模型類提供了默認的構造函數,允許創建一個空的模型對象,稍後可通過設置其屬性來補全內容。

2.2 設置留言內容

comment.content = request.POST.get('content')  # 取得表單中的留言內容
  • request.POST.get('content') 的作用
    • 從用戶提交的表單中獲取 content 欄位的值。
    • 該值對應於留言表單中的 <textarea name="content">
  • 處理數據的注意點
    • get() 方法在鍵不存在時不會拋出錯誤,而是返回 None,這樣可以避免程序崩潰。
    • 如果需要處理必填欄位,建議在提交表單之前先進行表單驗證。

2.3 設置關聯的履歷 ID

comment.resume_id = resume
  • 作用
    • 通過設置 resume_id,將該留言與目標履歷進行關聯。
    • resume_idComment 模型中的外鍵欄位。
  • 背後邏輯
    • 在 Django 中,外鍵欄位用於建立模型之間的關聯。
    • 這裡的 resume_id 是一個整數,對應 Resume 模型的主鍵(id 欄位)。

2.4 保存到資料庫

comment.save()
  • 作用:將已設置好屬性的 Comment 實例保存到資料庫中。
  • 背後邏輯
    1. 查詢轉換:Django 自動將模型對象的屬性轉換為 SQL INSERT 語句。
      • 例如,這段代碼會執行類似於以下的 SQL 操作: INSERT INTO comments_comment (content, resume_id, created_at) VALUES ('留言內容', 1, '2024-11-21 12:00:00');
    2. 驗證與保存save() 方法會先檢查所有欄位是否符合模型定義的要求(如必填欄位、最大長度等),然後將數據保存到資料庫中。

顯示成功訊息

messages.success(request, "新增留言成功!")
  • 作用:通知用戶操作已成功完成。
  • 背後邏輯
    • Django 的 messages 框架提供了一種簡單的方式來顯示用戶友好的通知。
    • 這段代碼會將一條 “新增留言成功!” 的消息添加到會話中,稍後可以在模板中顯示

重定向至原始履歷頁面

return redirect('resumes:show', id=resume.id)
  • 作用
    • 使用 redirect 函數將用戶導回原始履歷頁面。
    • 避免用戶在刷新頁面時重新提交表單,導致重複數據。
  • 背後邏輯
    1. 路由名稱解析'resumes:show' 是路由的命名空間,指向目標頁面的路由。
    2. URL 生成
      • 通過傳遞 id=resume.id,動態生成目標 URL,例如:http://localhost:8000/resumes/1/

補充:Django 外鍵運作機制

1. 外鍵的本質

外鍵(ForeignKey)是一種資料庫關聯欄位,用來建立表與表之間的關聯。

在 Django 中,當你在模型中定義外鍵時:

class Comment(models.Model):
    resume = models.ForeignKey(Resume, on_delete=models.CASCADE)
  • resume 欄位在 Python 層面上是一個 對應的模型實例(Resume,讓你可以方便地存取或操作整個 Resume 資料。
  • 在資料庫層面,外鍵欄位實際上只會儲存被關聯表的主鍵(Resumeid 值),這樣可以節省空間並加速查詢。
2. Django 為什麼只儲存主鍵(id

當你執行以下程式碼:

comment.resume = resume

Django 會自動提取 resume.id,並將其儲存在資料庫中對應的外鍵欄位中(通常命名為 resume_id)。

原因如下:

  1. 資料庫的最佳實踐
    關聯式資料庫設計中,外鍵的本質是儲存被參照資料表的主鍵,這樣可以保持資料結構的簡潔,並避免重複儲存同樣的資料。
  2. 外鍵欄位的設計
    在資料庫中,外鍵只需要指向關聯表的一條記錄。通過儲存主鍵(例如 resume.id),可以快速查詢到完整的 Resume 資料。
  3. 節省空間與效率
    如果外鍵欄位直接儲存整個 Resume 資料(例如 titleskill 等),不僅會浪費儲存空間,還會讓資料冗餘並難以維護。
3. Django 的物件關聯

儘管資料庫層面只儲存 resume_id,Django 幫助你隱藏了這些細節,讓你可以直接操作模型實例:

  • 取值時:當你存取 comment.resume 時,Django 會自動幫你查詢資料庫,並返回與該 resume_id 對應的 Resume 實例。
  • 賦值時:當你將 Resume 實例賦值給 comment.resume 時,Django 只會提取並儲存主鍵 resume.id

留言功能的完整流程與順序

以下是新增留言功能的詳細流程,涵蓋從頁面顯示、表單提交到視圖處理與頁面轉址的每一步邏輯與細節。

目錄結構

Project/
├── resumes/
│   ├── templates/
│   │   ├── resumes/
│   │   │   ├── show.html
│   ├── urls.py
│   ├── views/
│   │   ├── comment_views.py
├── db.sqlite3

步驟 1: 表單設計

目錄指引

目前位置:templates/resumes/show.html

操作內容

  • show.html 中新增留言表單,用於提交用戶輸入內容。
  • 代碼:
<form action="{% url 'resumes:comments' resume.id %}" method="post">
    {% csrf_token %}
    <textarea name="content"></textarea>
    <button>新增留言</button>
</form>

步驟 2: 設置路由

目錄指引

目前位置:resumes/urls.py

操作內容

  • urls.py 中新增路由,匹配表單提交的目標地址:
path("<int:id>/comments", comment_views.index, name="comments")

步驟 3: 表單提交與處理

目錄指引

目前位置:views/comment_views.py

操作內容

  • 處理 POST 請求,執行以下步驟:
  1. 檢索履歷:resume = get_object_or_404(Resume, id=id)
    • 查找 ID 對應的履歷,若找不到則返回 404。
  2. 保存留言: comment = Comment() comment.content = request.POST.get('content') # 提取內容 comment.resume_id = resume.id # 關聯到履歷 comment.save() # 保存
  3. 顯示成功訊息並重定向: messages.success(request, "新增留言成功!") return redirect('resumes:show', id=resume.id)

步驟 4: 頁面轉址與成功提示

目錄指引

目前位置:templates/resumes/show.html

操作內容

  • 在表單提交後,頁面會重定向到原履歷顯示頁面。
  • 用戶體驗改進:
    1. 成功訊息展示:「新增留言成功!」
    2. 頁面即時顯示新增的留言內容。

完整流程示意圖

以下為操作邏輯的圖示:

1. 表單設計 (show.html)
   
2. 設置路由 (urls.py)
   
3. 視圖處理 (comment_views.py)
   
4. 頁面轉址 (show.html)
  • 每一步操作對應的檔案及邏輯處理清晰呈現,便於快速理解與實踐。

小結

  1. 留言功能的實現
    • 配置 Resumes 與 Comments 的應用與路由,實現跨應用協作。
  2. 表單提交與視圖處理
    • 使用 POST 方法與視圖邏輯保存留言。

留言功能不僅增強了網站的互動性,也展示了 Django 應用之間的高效協作方式。

透過本文,期望你能熟悉表單提交、跨應用視圖調用與功能優化的實踐!

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *