ModelForm
如果表單的欄位剛好對應到某個 Model(例如「新增文章」表單對應 Post 這個 Model),逐一手寫 forms.CharField()、forms.DateField() 等於把 Model 的欄位定義重複寫一次。ModelForm 可以直接依 Model 的欄位自動產生表單,並且 save() 之後能直接寫回資料庫,省掉重複定義與手動存檔的步驟。
官方文件:Creating forms from models,裡面也整理了 Form 欄位與 Model 欄位的對應關係、如何覆寫預設欄位。
定義 ModelForm
用 class Meta 指定要對應的 model,以及要放進表單的 fields:
# forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ["title", "content"]
fields 也可以寫成 "__all__" 代表包含所有欄位,但正式專案建議明確列出需要的欄位,避免不小心把不該讓使用者填寫的欄位(例如 author、created_at)也開放出去。
在 View 中新增與修改資料
新增資料的寫法與一般 Form 幾乎一樣,差別在 form.save() 可以直接把驗證後的資料寫進資料庫,不需要再手動組出 Model 實例:
# views.py
from django.shortcuts import redirect, render
from .forms import PostForm
def create_post(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
form.save()
return redirect("post-list")
else:
form = PostForm()
return render(request, "myapp/post_form.html", {"form": form})
修改既有資料時,把要編輯的物件傳進 instance 參數,表單會自動帶入該物件目前的值,save() 也會更新同一筆資料而不是新增一筆:
def update_post(request, pk):
post = Post.objects.get(pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
form.save()
return redirect("post-list")
else:
form = PostForm(instance=post)
return render(request, "myapp/post_form.html", {"form": form})
用 Class-Based Views 進一步簡化
上面「新增/修改」的樣板程式碼其實高度重複,Django 內建的 CreateView、UpdateView 可以把這套流程再濃縮成幾行類別屬性,詳見 Class-Based Views。
下一步
表單處理熟悉之後,可以進一步用 Class-Based Views 大幅減少 CRUD(新增、讀取、修改、刪除)介面的重複程式碼。