Django Forms
官方文件:Working with forms、Form and field validation。
先看原生 HTML 表單的問題
在導入 Django Forms 之前,先看看純手刻表單長什麼樣子,會更容易理解 Django Forms 省下了哪些工。
GET 方法(資料會顯示在網址上,適合搜尋、篩選這類不改變伺服器狀態的操作):
<form action="/my_view/" method="get">
<label for="your_name">Your name: </label>
<input type="text" name="your_name">
<input type="submit" value="OK">
</form>
def my_view(request):
if "your_name" in request.GET and request.GET["your_name"] != "":
return HttpResponse("Hello, " + request.GET["your_name"])
else:
return HttpResponse("Hello, John Doe!")
POST 方法(資料放在請求主體中,適合會改變伺服器狀態的操作,例如新增、修改資料)。注意 {% csrf_token %} 這個標籤,它是 Django 防止跨站請求偽造攻擊的機制,所有 POST 表單都必須加上:
<form action="/my_view/" method="post">
{% csrf_token %}
<label for="your_name">Your name: </label>
<input type="text" name="your_name">
<input type="submit" value="OK">
</form>
def my_view(request):
if request.POST:
if request.POST["your_name"]:
return HttpResponse("Hello, " + request.POST["your_name"])
else:
return HttpResponse("Hello, John Doe!")
else:
return HttpResponse("Hello, John Doe!")
可以看到即使只有一個欄位,驗證邏輯(是否為空)就得自己判斷,欄位一多,程式碼會迅速變得又長又重複。
定義 Django Form
用 forms.Form 定義一次欄位,Django 就能自動產生對應的 HTML、驗證資料、並提供錯誤訊息:
# forms.py
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label="Your name", max_length=100)
標準的 View 寫法
處理表單的 View,通常遵循「GET 顯示空表單、POST 驗證並處理資料」的固定套路:
# views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
if request.method == "POST":
# 用送出的資料建立表單實例
form = NameForm(request.POST)
# is_valid() 會執行所有欄位的驗證規則
if form.is_valid():
# 驗證通過的資料在 form.cleaned_data 裡
# ...
return HttpResponseRedirect("/thanks/")
else:
# GET 請求:顯示一個空白表單
form = NameForm()
return render(request, "name.html", {"form": form})
在 Template 中輸出表單
最簡單的方式是直接輸出 {{ form }},Django 會自動產生每個欄位對應的 HTML:
<!-- name.html -->
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
若需要客製每個欄位的外觀(例如加 CSS class、調整排版),可以逐欄位迴圈輸出,field.errors 會顯示該欄位的驗證錯誤訊息:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
下一步
如果表單欄位剛好對應到某個 Model,可以用 ModelForm 省掉重複定義欄位的工。