我們前面都是手工在HTML文件中編寫表單form元素,然后在views.py的視圖函數中接收表單中的用戶數據,再編寫驗證代碼進行驗證,最后使用ORM進行數據庫的增刪改查。這樣費時費力,整個過程比較復雜,而且有可能寫得不太恰當,數據驗證也比較麻煩。
設想一下,如果我們的表單擁有幾十上百個數據字段,有不同的數據特點,如果也使用手工的方式,其效率和正確性都將無法得到保障。
有鑒於此,Django在內部集成了一個表單功能,以面向對象的方式,直接使用Python代碼生成HTML表單代碼,專門幫助我們快速處理表單相關的內容。
Django的表單給我們提供了下面三個主要功能:
- 准備和重構數據用於頁面渲染;
- 為數據創建HTML表單元素;
- 接收和處理用戶從表單發送過來的數據。
編寫Django的form表單,非常類似我們在模型系統里編寫一個模型。
在模型中,一個字段代表數據表的一列,而form表單中的一個字段代表<form>中的一個<input>元素。
一、創建表單模型
在項目根目錄的login文件夾下,新建一個forms.py文件,也就是/login/forms.py,又是我們熟悉的Django組織文件的套路,一個app一套班子!
在/login/forms.py中寫入下面的代碼,是不是有一種編寫數據model模型的既視感?
from django import forms
class UserForm(forms.Form):
username = forms.CharField(label="用戶名", max_length=128)
password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput)
說明:
- 要先導入forms模塊
- 所有的表單類都要繼承forms.Form類
- 每個表單字段都有自己的字段類型比如CharField,它們分別對應一種HTML語言中
<form>內的一個input元素。這一點和Django模型系統的設計非常相似。 - label參數用於設置
<label>標簽 max_length限制字段輸入的最大長度。它同時起到兩個作用,一是在瀏覽器頁面限制用戶輸入不可超過字符數,二是在后端服務器驗證用戶輸入的長度也不可超過。widget=forms.PasswordInput用於指定該字段在form表單里表現為<input type='password' />,也就是密碼輸入框。
二、修改視圖
使用了Django的表單后,就要在視圖中進行相應的修改:
from django.shortcuts import render
from django.shortcuts import redirect
from . import models
from django import forms
from login import forms
def index(request):
pass
return render(request, 'login/index.html')
def login(request):
if request.method == "POST":
login_form = forms.UserForm(request.POST)
message = "請檢查填寫的內容!"
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
try:
user = models.User.objects.get(name=username)
if user.password == password:
return redirect('/index/')
else:
message = "密碼不正確!"
except:
message = "用戶不存在!"
return render(request, 'login/login.html', locals())
login_form = forms.UserForm()
return render(request, 'login/login.html', locals())
def register(request):
pass
return render(request, 'login/register.html')
def logout(request):
pass
return redirect("/index/")
說明:
- 對於非POST方法發送數據時,比如GET方法請求頁面,返回空的表單,讓用戶可以填入數據;
- 對於POST方法,接收表單數據,並驗證;
- 使用表單類自帶的
is_valid()方法一步完成數據驗證工作; - 驗證成功后可以從表單對象的
cleaned_data數據字典中獲取表單的具體值; - 如果驗證不通過,則返回一個包含先前數據的表單給前端頁面,方便用戶修改。也就是說,它會幫你保留先前填寫的數據內容,而不是返回一個空表!
另外,這里使用了一個小技巧,Python內置了一個locals()函數,它返回當前所有的本地變量字典,我們可以偷懶的將這作為render函數的數據字典參數值,就不用費勁去構造一個形如{'message':message, 'login_form':login_form}的字典了。這樣做的好處當然是大大方便了我們,但是同時也可能往模板傳入了一些多余的變量數據,造成數據冗余降低效率。
三、 修改login頁面
Django的表單很重要的一個功能就是自動生成HTML的form表單內容。現在,我們需要修改一下原來的login.html文件:
{% extends 'login/base.html' %}
{% load staticfiles %}
{% block title %}登錄{% endblock %}
{% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}
{% block content %}
<div class="container">
<div class="col-md-4 col-md-offset-4">
<form class='form-login' action="/login/" method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h2 class="text-center">歡迎登錄</h2>
{{ login_form }}
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
</div>
</div> <!-- /container -->
{% endblock %}
說明:
- 你沒有看錯!一個
{{ login_form }}就直接完成了表單內容的生成工作!login_form這個名稱來自你在視圖函數中生成的form實例的變量名! - 但是,它不會生成
<form>...</form>標簽,這個要自己寫; - 使用POST的方法時,必須添加
{% csrf_token %}標簽,用於處理csrf安全機制; - Django自動為每個input元素設置了一個id名稱,對應label的for參數
- 重置和提交按鈕需要自己寫,Django不會幫你生成!
我們到瀏覽器中,看下實際生成的html源碼是什么:
<form class='form-login' action="/login/" method="post">
<div class="alert alert-warning">密碼不正確!</div>
<input type='hidden' name='csrfmiddlewaretoken' value='t7MdqJzR7fbiDth5ZQSBpHb22F8sUkjTy32MlEuhXdW8EZPTwcTNuF0PPOHlxKPz' />
<h2 class="text-center">歡迎登錄</h2>
<tr><th><label for="id_username">用戶名:</label></th><td><input type="text" name="username" value="jack" maxlength="128" required id="id_username" /></td></tr>
<tr><th><label for="id_password">密碼:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password" /></td></tr>
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
也就是說,Django的form表單功能,幫你自動生成了下面部分的代碼:
<tr><th><label for="id_username">用戶名:</label></th><td><input type="text" name="username" value="jack" maxlength="128" required id="id_username" /></td></tr>
<tr><th><label for="id_password">密碼:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password" /></td></tr>
這看起來好像一個<table>標簽啊?沒錯,就是<table>標簽,而且是不帶<table></table>的,捂臉!
實際上除了通過{{ login_form }}簡單地將表單渲染到HTML頁面中了,還有下面幾種方式:
{{ login_form.as_table }}將表單渲染成一個表格元素,每個輸入框作為一個<tr>標簽{{ login_form.as_p }}將表單的每個輸入框包裹在一個<p>標簽內{{ login_form.as_ul }}將表單渲染成一個列表元素,每個輸入框作為一個<li>標簽
注意:上面的渲染方法中都要自己手動編寫<table>或者<ul>標簽。
重新啟動服務器,刷新頁面,如下圖所示:

四、手動渲染表單字段
直接{{ login_form }}雖然好,啥都不用操心,但是界面真的很丑,往往並不是你想要的,如果你要使用CSS和JS,比如你要引入Bootstarps框架,這些都需要對表單內的input元素進行額外控制,那怎么辦呢?手動渲染字段就可以了。
可以通過{{ login_form.name_of_field }}獲取每一個字段,然后分別渲染,如下例所示:
<div class="form-group">
{{ login_form.username.label_tag }}
{{ login_form.username}}
</div>
<div class="form-group">
{{ login_form.password.label_tag }}
{{ login_form.password }}
</div>
其中的label標簽可以用label_tag方法來生成。這樣子更加靈活了,但是靈活的代價就是我們要寫更多的代碼,又偏向原生的HTML代碼多了一點。
但是問題又...又...又來了!刷新登錄頁面,卻是下圖的樣子:

好像Bootstrap沒有生效呀!仔細查看最終生成的頁面源碼,你會發現,input元素里少了一個CSS的類form-control。這可咋辦?
在form類里添加attr屬性即可,如下所示修改login/forms.py
from django import forms
class UserForm(forms.Form):
username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
再次刷新頁面,我們熟悉的Bootstarp框架UI又回來了!
實際上,Django針對{{ login_form }}表單,提供了很多靈活的模板語法,可以循環,可以取值,可以針對可見和不可見的部分單獨控制,詳細內容可以參考教程前面的章節。

