Django 實現 CRUD 的 C(Create):新增資料功能完整指南

更新日期: 2024 年 11 月 24 日

在 Django 中,實現資料的新增功能(Create)是 CRUD 操作的第一步。

本文將帶你詳細了解如何設置路由、定義視圖和配置表單來完成資料新增功能。

同時詳細解析表單提交、資料存取及頁面轉址的實現細節。

幫助你建立穩定且高效的新增功能。


專案目錄結構

在開始操作之前,請確認你的專案目錄結構如下:

mysite/
├── manage.py
├── mysite/
   ├── settings.py       # 專案配置文件
   ├── urls.py           # 主路由配置

├── resumes/
   ├── __init__.py
   ├── admin.py
   ├── apps.py
   ├── models.py         # 定義 Resume 模型
   ├── views.py          # 定義新增資料的視圖邏輯
   ├── urls.py           # Resumes 應用的路由
   ├── templates/
       ├── resumes/
           ├── home.html  # 資料首頁
           ├── new.html   # 新增資料表單
├── db.sqlite3            # 預設的 SQLite 資料庫

配置路由與視圖

在新增資料功能中,路由負責管理 URL,視圖負責處理邏輯。

我們需要設置首頁路由(處理資料顯示和提交)及新增資料的路由。

配置路由

resumes/urls.py 中新增以下路由設定:

from django.urls import path
from . import views

app_name = 'resumes'

urlpatterns = [
    path("", views.home, name='list'),       # 主頁,顯示所有資料
    path("new/", views.new, name='nn')      # 新增資料頁面
]

定義視圖

resumes/views.py 中定義處理邏輯:

主頁(資料顯示與提交處理)

from django.shortcuts import render, redirect
from .models import Resume

def home(request):
    if request.method == "POST":
        # 處理表單提交
        resume = Resume()
        resume.title = request.POST["title"]
        resume.skill = request.POST["skill"]
        resume.content = request.POST["content"]
        resume.save()
        return redirect("resumes:list")  # 轉址回主頁

新增資料頁面

def new(request):
    return render(request, "resumes/new.html")

配置新增資料的表單

resumes/templates/resumes/new.html 中設置表單,供使用者填寫資料。

表單範例

{% extends "shared/layout.html" %}

{% block content %}
<h1>新增 Resume</h1>

<form action="{% url 'resumes:list' %}" method="POST">
    {% csrf_token %}
    title: <br />
    <input type="text" name="title" /> <br />

    skill: <br />
    <input type="text" name="skill" /> <br />

    content: <br />
    <textarea name="content"></textarea> <br />

    <button type="submit">新增</button>
</form>
{% endblock %}

表單的關鍵設計

表單的提交路徑

<form action="{% url 'resumes:list' %}" method="POST">

表單的提交路徑與設計解析

在 Django 中,表單的提交路徑由 action 屬性指定,它告訴瀏覽器該將表單資料提交到哪個 URL。

而在 Django 模板中,{% url %} 是用於生成動態 URL 的模板語法,能夠根據路由名稱生成對應的路徑。

表單路徑範例

<form action="{% url 'resumes:list' %}" method="POST">

生成的路徑解析

  1. {% url 'resumes:list' %}
    • 這段代碼會根據 Django 路由配置中定義的命名路由(resumes:list)動態生成 URL。
    • 在本例中,resumes:list 對應於 resumes/urls.py 中的路由: path("", views.home, name='list') 根據此設定,{% url 'resumes:list' %} 會生成對應的絕對路徑。
    • 假設專案運行在 http://localhost:8000/,且應用路徑為 resumes,則生成的絕對路徑為: http://localhost:8000/resumes/
  2. 表單提交的目的
    • 表單提交到 http://localhost:8000/resumes/
    • 該路徑由首頁視圖 views.home 處理,並通過 HTTP POST 方法接收資料。

設計原則與 RESTful API

  1. 符合 RESTful API
    • 在 RESTful API 中,POST 方法通常用於新增資源。

      因此,表單通過 POST 方法提交資料,與 RESTful 設計原則保持一致。
    • 例如,http://localhost:8000/resumes/ 表示資源的集合,提交 POST 請求表示向集合中新增一項資源。
  2. 清晰且結構化的路徑設計
    • 根據路由規範,首頁路徑 /resumes/ 是資源的入口。
    • 與此類似,其他操作的路徑可以設計為:
      • /resumes/new/:顯示新增資源的表單頁面。
      • /resumes/<id>/:顯示單一資源的詳細資訊。
  3. 動態路徑的優勢
    • 使用 {% url %} 可以避免硬編碼路徑,當路由名稱或結構改變時,只需修改 urls.py 即可,不需要逐一更改所有模板中的路徑,減少維護成本。

表單的 CSRF 保護

在 Web 安全中,CSRF(Cross-Site Request Forgery,跨站請求偽造)是一種常見的攻擊方式。

攻擊者利用已認證使用者的身份,冒用其權限執行未經授權的操作,對系統安全造成威脅。

Django 通過內建的 CSRF 防護機制,有效防止這類攻擊。

什麼是 CSRF?

CSRF 攻擊的核心在於欺騙目標使用者的瀏覽器向受害網站發送請求。

例如:

  • 使用者 A 登錄到銀行網站後,瀏覽器保存了用戶的對話資訊(Cookie)。
  • 攻擊者引誘使用者 A 點擊某個惡意連結,該連結會模擬使用者A 的操作,向銀行網站發送轉賬請求。
  • 因為使用者已經登錄,受害網站無法區分該請求是使用者合法發出的,還是攻擊者偽造的。

Django 的 CSRF 防護機制

Django 提供了自動化的 CSRF 保護,通過 CSRF 中間件CSRF 標籤{% csrf_token %})進行安全性檢查。其核心思路是:

  1. 生成 CSRF Token
    • 每次使用者訪問受保護的表單頁面時,Django 會生成一個唯一的 CSRF Token 並嵌入到表單中。
  2. 校驗 CSRF Token
    • 當使用者提交表單時,後端會檢查請求中包含的 CSRF Token 是否與伺服器生成的匹配。
    • 如果匹配,請求被認可;如果不匹配或缺少,請求將被拒絕。

如何使用 CSRF 保護

在表單中加入 {% csrf_token %} 標籤,是啟用 CSRF 保護的必要步驟。

表單範例

以下是一個包含 CSRF 保護的 Django 表單:

<form action="{% url 'resumes:list' %}" method="POST">
    {% csrf_token %}
    <label for="title">Title:</label>
    <input type="text" name="title" id="title">

    <label for="skill">Skill:</label>
    <input type="text" name="skill" id="skill">

    <label for="content">Content:</label>
    <textarea name="content" id="content"></textarea>

    <button type="submit">新增</button>
</form>
說明
  1. {% csrf_token %}
    • 生成唯一的安全令牌,嵌入到表單內部。
    • 該令牌將與請求一起發送到伺服器,用於驗證請求的合法性。
  2. 作用範圍
    • 適用於所有提交方式為 POST 的表單。
    • GET 請求通常不修改伺服器狀態,因此不需要 CSRF 保護。

CSRF 防護的工作流程

  1. 生成 Token
    • 使用者訪問表單頁面時,伺服器會生成一個隨機的 CSRF Token,並將其存儲在伺服器端的對話中。
    • 該 Token 會同步嵌入到表單中,作為隱藏欄位提交給前端。
  2. 提交表單
    • 使用者填寫表單並提交,CSRF Token 隨請求一起發送到伺服器。
  3. 伺服器驗證
    • Django 比較請求中的 CSRF Token 與伺服器端存儲的 Token。
    • 若令牌一致,請求被認可;否則,請求被拒絕,返回錯誤提示。

常見的 CSRF 錯誤與解決方法

錯誤提示

如果表單未正確包含 CSRF Token,Django 會返回以下錯誤:

Forbidden (403) CSRF verification failed. Request aborted.
解決方法
  • 確保在表單內部添加 {% csrf_token %}{% csrf_token %}

運作機制解析

表單提交到資料庫的流程

提交表單

  • 使用者在前端輸入資料,表單中的 name 屬性對應資料的鍵,輸入內容為值,例如:
 <input type="text" name="title" />
  • 當表單被提交時,所有 name 屬性及其值會作為鍵值對,通過 HTTP POST 方法傳送到後端。

接收資料

  • 在視圖中,request.POST 是一個類似字典的物件,包含表單提交的資料。
  • 使用鍵值對提取數據:
title = request.POST['title'] 
skill = request.POST['skill'] 
content = request.POST['content']

保存資料到資料庫

Django 提供了 ORM(Object-Relational Mapping),可以通過操作物件的方式與資料庫交互:

  1. 創建模型實例:
resume = Resume()
  1. 為屬性賦值:
 resume.title = title 
 resume.skill = skill 
 resume.content = content
  1. 調用 .save() 方法保存到資料庫:
resume.save()
  1. 以上操作會自動生成對應的 SQL 指令並執行,無需手動書寫 SQL。

資料提交後的頁面轉址

為什麼需要轉址?

  • 如果表單提交後刷新頁面,瀏覽器會重新執行上一次操作,導致資料重複提交。
  • 轉址可以避免這種情況。

轉址的實現

在視圖中使用 redirect 方法,將使用者導向主頁:

return redirect("resumes:list")
  • 含義
    • resumes:list 是一個命名路由,用於指向應用中配置的路徑。
    • redirect("resumes:list") 指示 Django 返回一個 HTTP 302 重定向響應,告訴瀏覽器向指定的 URL 發送新的 GET 請求。
  • 來源
    • resumes:list 來自於應用的 urls.py 文件,具體如下:
      • app_name = 'resumes' 定義了該應用的命名空間。
      • name='list' 給路由 path("", views.home) 分配了一個名稱,便於在模板或視圖中引用。
# resumes/urls.py
app_name = 'resumes'

urlpatterns = [
    path("", views.home, name='list'),  # 主頁,顯示所有資料
    path("new/", views.new, name='nn'),  # 新增資料頁面
]
  • 對應路徑
    • 當引用 resumes:list 時,Django 將解析為 /resumes/(假設該應用掛載於主路由的 /resumes/ 下)。

為何 resumes:list 可以直接引用?

resumes:list 的直接引用得益於 Django 的路由反向解析機制。

  • 路由反向解析的工作原理
    1. 路由註冊
      • Django 啟動時,會自動讀取項目中所有應用的 urls.py 文件,將每個路由註冊到內存中的路由解析器中。
    2. 命名路由的綁定
      • 將每個路由的名稱(如 list)與其命名空間(如 resumes)組合,形成唯一的標識符(如 resumes:list)。
    3. 反向查找
      • 當視圖或模板中引用 resumes:list 時,Django 會根據該標識符查找對應的路徑,並將其解析為具體的 URL(如 /resumes/)。
  • 未經宣告即可引用的原因
    • 雖然 resumes:list 看似未在 views.py 中明確宣告,但它是通過 Django 的全局路由解析器處理的。
    • Django 自動管理 urls.pyviews.py 的聯繫,無需手動導入命名路由。

redirect 的完整運作流程

以下是 redirect("resumes:list") 的運作過程:

  1. 表單提交
    • 用戶填寫表單並提交,瀏覽器發送一個 POST 請求 至後端。
  2. 處理資料
    • 視圖中接收請求,驗證並保存資料。
    • 使用 redirect("resumes:list") 返回一個 HTTP 302 響應。
  3. 重定向請求
    • 瀏覽器接收到 302 響應後,按照指定的 URL(如 /resumes/)發送新的 GET 請求
  4. 渲染頁面
    • 後端處理 GET 請求,返回首頁內容,完成轉址流程。

為何使用 redirect

  • 防止重複提交
    • 用戶刷新頁面時,瀏覽器會重新發送上一個請求。若不使用重定向,用戶的 POST 請求可能會被重複處理,導致重複提交資料。
    • 使用 redirect 將用戶導向新的頁面(GET 請求),即可避免此問題。
  • 提升用戶體驗
    • 提交成功後立即跳轉到目標頁面(如主頁),用戶能夠快速看到更新後的結果。

完整目錄結構示意圖

完成新增功能後的目錄結構如下:

mysite/
├── manage.py
├── mysite/
   ├── settings.py
   ├── urls.py

├── resumes/
   ├── __init__.py
   ├── admin.py
   ├── apps.py
   ├── models.py
   ├── views.py          # 定義資料新增的邏輯
   ├── urls.py           # 配置應用的路由
   ├── templates/
       ├── resumes/
           ├── home.html  # 資料首頁
           ├── new.html   # 新增資料表單
├── db.sqlite3

小結

通過本篇內容,我們實現了 Django CRUD 的 C(Create)操作:

  1. 設置路由與視圖:新增資料的功能通過首頁處理表單提交。
  2. 表單設置:設置包含 CSRF 保護的表單,用於接收使用者輸入。
  3. 運作機制解析:從表單提交到資料庫存取的完整流程。
  4. 頁面轉址:使用 redirect 避免重複提交,提升使用者體驗。

這套新增功能的實現,為後續的 CRUD 操作(讀取、更新、刪除)奠定了基礎!

Similar Posts

發佈留言

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