Django 實現 CRUD 的 C(Create):新增資料功能完整指南
更新日期: 2024 年 11 月 24 日
本文為 Django 進階教學,第 6 篇:
- 理解 Django 中的相對路徑與絕對路徑
- Django URL 路徑設置|name 參數與命名空間(Namespace)
- Django 串接資料庫與模型建構:完整入門指南
- Django 模型遷移與資料庫同步完整指南
- 使用 Django ORM 將資料寫入資料庫:新手入門指南
- Django 實現 CRUD 的 C(Create):新增資料功能完整指南 👈 所在位置
- Django CRUD 的 R(Read):資料讀取與顯示功能指南
- Django CRUD 的 U(Update):資料更新功能完整指南
- Django CRUD 的 D(Delete):資料刪除功能完整指南
建議閱讀本文前,先閱讀完 Django 新手教學 系列文
在 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">
生成的路徑解析
{% 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/
- 這段代碼會根據 Django 路由配置中定義的命名路由(
- 表單提交的目的:
- 表單提交到
http://localhost:8000/resumes/
。 - 該路徑由首頁視圖
views.home
處理,並通過 HTTP POST 方法接收資料。
- 表單提交到
設計原則與 RESTful API
- 符合 RESTful API:
- 在 RESTful API 中,POST 方法通常用於新增資源。
因此,表單通過 POST 方法提交資料,與 RESTful 設計原則保持一致。 - 例如,
http://localhost:8000/resumes/
表示資源的集合,提交 POST 請求表示向集合中新增一項資源。
- 在 RESTful API 中,POST 方法通常用於新增資源。
- 清晰且結構化的路徑設計:
- 根據路由規範,首頁路徑
/resumes/
是資源的入口。 - 與此類似,其他操作的路徑可以設計為:
/resumes/new/
:顯示新增資源的表單頁面。/resumes/<id>/
:顯示單一資源的詳細資訊。
- 根據路由規範,首頁路徑
- 動態路徑的優勢:
- 使用
{% 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 %}
)進行安全性檢查。其核心思路是:
- 生成 CSRF Token:
- 每次使用者訪問受保護的表單頁面時,Django 會生成一個唯一的 CSRF Token 並嵌入到表單中。
- 校驗 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>
說明
{% csrf_token %}
:- 生成唯一的安全令牌,嵌入到表單內部。
- 該令牌將與請求一起發送到伺服器,用於驗證請求的合法性。
- 作用範圍:
- 適用於所有提交方式為
POST
的表單。 - GET 請求通常不修改伺服器狀態,因此不需要 CSRF 保護。
- 適用於所有提交方式為
CSRF 防護的工作流程
- 生成 Token:
- 使用者訪問表單頁面時,伺服器會生成一個隨機的 CSRF Token,並將其存儲在伺服器端的對話中。
- 該 Token 會同步嵌入到表單中,作為隱藏欄位提交給前端。
- 提交表單:
- 使用者填寫表單並提交,CSRF Token 隨請求一起發送到伺服器。
- 伺服器驗證:
- 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),可以通過操作物件的方式與資料庫交互:
- 創建模型實例:
resume = Resume()
- 為屬性賦值:
resume.title = title
resume.skill = skill
resume.content = content
- 調用
.save()
方法保存到資料庫:
resume.save()
- 以上操作會自動生成對應的 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 的路由反向解析機制。
- 路由反向解析的工作原理:
- 路由註冊:
- Django 啟動時,會自動讀取項目中所有應用的
urls.py
文件,將每個路由註冊到內存中的路由解析器中。
- Django 啟動時,會自動讀取項目中所有應用的
- 命名路由的綁定:
- 將每個路由的名稱(如
list
)與其命名空間(如resumes
)組合,形成唯一的標識符(如resumes:list
)。
- 將每個路由的名稱(如
- 反向查找:
- 當視圖或模板中引用
resumes:list
時,Django 會根據該標識符查找對應的路徑,並將其解析為具體的 URL(如/resumes/
)。
- 當視圖或模板中引用
- 路由註冊:
- 未經宣告即可引用的原因:
- 雖然
resumes:list
看似未在views.py
中明確宣告,但它是通過 Django 的全局路由解析器處理的。 - Django 自動管理
urls.py
與views.py
的聯繫,無需手動導入命名路由。
- 雖然
redirect
的完整運作流程
以下是 redirect("resumes:list")
的運作過程:
- 表單提交:
- 用戶填寫表單並提交,瀏覽器發送一個 POST 請求 至後端。
- 處理資料:
- 視圖中接收請求,驗證並保存資料。
- 使用
redirect("resumes:list")
返回一個 HTTP 302 響應。
- 重定向請求:
- 瀏覽器接收到 302 響應後,按照指定的 URL(如
/resumes/
)發送新的 GET 請求。
- 瀏覽器接收到 302 響應後,按照指定的 URL(如
- 渲染頁面:
- 後端處理 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)操作:
- 設置路由與視圖:新增資料的功能通過首頁處理表單提交。
- 表單設置:設置包含 CSRF 保護的表單,用於接收使用者輸入。
- 運作機制解析:從表單提交到資料庫存取的完整流程。
- 頁面轉址:使用
redirect
避免重複提交,提升使用者體驗。
這套新增功能的實現,為後續的 CRUD 操作(讀取、更新、刪除)奠定了基礎!