Django表單之使用表單
本文轉載自http://www.liujiangblog.com/course/django/152
,供記錄學習使用。
假設你想從表單接收使用者名稱資料,一般情況下,你需要在HTML中手動編寫一個如下的表單元素:
<form action="/your-name/" method="post"> <label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" value="{{ current_name }}"> <input type="submit" value="OK"> </form>
<form action='/your-name/" method = "post">
這一行定義了我們的傳送目的地/your-name/
和HTTP方法POST
。form元素內部還定義了一個說明標籤<label>
和一個傳送按鈕submit
,以及最關鍵的接收使用者輸入的<input>
元素。
一、 編寫表單類
我們可以通過Django提供的Form類來生成上面的表單,不在需要手動在HTML中編寫。
首先,在你當前app內建一個form.py
檔案,然後輸入下面的內容:
from django import forms class NameForm(forms.Form): your_name = forms.CharField(label='Your name',max_length=100)
要點:
- 提前匯入forms模組
- 所有的表單類都要繼承forms.Form類
- 每個表單欄位都有自己的欄位型別,比如CharField,它們分別對應一種HTML語言中的
<form>
元素中的表單元素。 - 例子中的label用於設定說明標籤
max_length
限制最大長度為100,。它同時起到兩個作用,一是在瀏覽器頁面限制使用者輸入不可超過100個字元,二是在後端伺服器驗證使用者輸入的長度不可超過100.
(警告:由於瀏覽器頁面可以被篡改、偽造、禁用、跳過的,所有的HTML手段的資料驗證只能防止意外不能防止惡意行為,是沒有安全保證的,破壞分子完全可以跳過瀏覽器的防禦手段偽造傳送請求!所以,在服務後端,必須將前端當做‘裸機’來對待,再次進完全徹底的資料驗證和安全防護!)
每個Django表單的例項都有一個內建的is_valid()
方法,來驗證接收資料是否合法。如果所有資料都合法,那麼該方法將返回True,並將所有的表單資料轉存到它的一個叫做cleaned_data
的屬性中,該屬性是一個字典型別的資料。
當我們將上面的表單渲染成真正的HTML元素,其內容如下:
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required />
一定要注意,它不包含<form>
標籤本身以及提交按鈕!!!,為什麼要這樣呢?方便你自己控制表單動作和css,js以及其它類似bootstrap框架的嵌入
二、檢視處理
需要在檢視中,例項化我們編寫好的表單類
# views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import NameForm
def get_name(request):
# 如果form通過POST方法傳送資料
if request.method == 'POST':
# 接受request.POST引數構造form類的例項
form = NameForm(request.POST)
# 驗證資料是否合法
if form.is_valid():
# 處理form.cleaned_data中的資料
# ...
# 重定向到一個新的URL
return HttpResponseRedirect('/thanks/')
# 如果是通過GET方法請求資料,返回一個空的表單
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
要點是:
- 對於GET方法請求頁面時,返回空的表單,讓使用者可以填寫資料;
- 對於POST方法,接收表單資料,並驗證;
- 如果資料合法,按照正常的業務邏輯繼續執行下去;
- 如果不合法,返回一個包含先前資料的表單給前端,方便使用者修改。
通過表單is_bound
屬性可以獲知一個表單已經綁定了資料,還是一個空表。
三、模板處理
在Django的模板中,我們只需要按下面處理,就可以得到完整的HTML頁面:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
要點:
<form>...</form>
標籤要自己寫- 使用POST的方法時必須新增
{% csrf_token %}
標籤,用於處理csrf安全機制 {{ form }}
代表Django為你生成其他所有的form標籤元素,也就是我們上面做的事情;- 提交按鈕需要手動新增
提示:預設情況下,Django支援HTML5的表單驗證功能,比如郵箱驗證、必填專案驗證等等。
四、高階技巧
上面的例子中,只有一個使用者名稱輸入框,太簡單了,實際上有更多的表單元素。看下面的例子:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Texarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
這個例子就有四個框組了。實際上Django的表單模組為我們內建了許多表單欄位,如下所示:
- BooleanField
- CharField
- ChoiceField
- TypedChoiceField
- DateField
- DateTimeField
- DecimalField
- DurationField
- EmailField
- FileField
- FilePathField
- FloatField
- ImageField
- IntegerField
- GenericlPAddressField
- MultipleChoiceField
- TypedMultipleChoiceField
- NullBooleanField
- RegexField
- SlugField
- TimeField
- URLField
- UUIDField
- ComboField
- MultiValueField
- SplitDateTimeField
- ModelChoiceField
- ModelMultipleChoiceField
每一個表單欄位型別都對應一種Widget類,每一種Widget類都對應了HTML語言中的一種input元素型別,比如<input type="text">
。需要在HTML中實際使用什麼型別的input,就需要在Django的表單欄位中選擇相應的field。比如要一個<input type="text">
,可以選擇一個CharField
.
一旦你的表單接收資料並驗證通過了,那麼就可以從form.cleaned_data
字典中讀取所有的表單資料,下面是一個例子:
#view.py
from django.core.mail import send_mail
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['[email protected]']
if cc_myself:
recipients.append(sender)
send_mail(subject,message,sender,recipients)
return HttpResponseRedirect('/thanks/')
五、使用表單模板
1.表單渲染格式
前面我們通過{{ form }}
模板語言,簡單地將表單渲染到HTML頁面中了,實際上,有更多的方式:
{{ form.as_table }}
將表單渲染成一個表格元素,每一個輸入框為一個<tr>
標籤{{ form.as_p}}
將表單的每一個輸入框包裹在一個<p>
標籤內tags{{ form.as_ul }}
將表單渲染成一個列表元素,每個輸入框作為一個<li>
標籤
注意:你要自己手動編寫<table>
和<url>
標籤。
下面是將上面的ContactForm作為{{ form.as_p }}
<p><label for="id_subject">Subject:</label>
<input id="id_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_message">Message:</label>
<textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
<input type="email" name="sender" id="id_sender" required /></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
注意:Django自動為每個input元素設定了一個id名稱,對應label的for引數
2.手動渲染表單欄位
直接{{ form }}
雖然好,啥都不用操心,但往往並不是你想要的,比如你要使用CSS和JS,比如你要引入Bootstraps框架,這些都需要對錶單內的input元素進行額外控制,需要手段渲染。
可以通過{{ form.name_of_field }}
獲取每一個欄位,然後分別渲染,如下例所示:
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="{{ form.sender.id_for_label }}">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
{{ form.cc_myself }}
</div>
其中的label標籤甚至可以使用label_tag()
方法來生成,於是可以簡寫成下面這個樣子:
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
</div>
這樣是不是更靈活了呢?但是靈活的代價就是我們需要寫更多的程式碼,又偏向於原生的HTML程式碼多了一點。
3.渲染表單錯誤資訊
注意上面的例子中,我們使用{{ form.name_of_field.errors }}
模板語法,在表單裡處理錯誤資訊。對於每一個表單欄位的錯誤,它其實會實際生成一個無序列表,參考下面的樣子:
<ul class="errorlist">
<li>Sender is required.</li>
</ul>
這個列表有一個預設的css樣式類errorlist
,如果你想進一步定製這個樣式,可以迴圈錯誤列表裡面的內容,然後單獨設定樣式:
{% if form.subject.errors %}
<ol>
{% for error in form.subject.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
一切非欄位的錯誤資訊,比如表單錯誤,隱藏欄位的錯誤都儲存在{{ form.non_field_errors }}
中,上面的例子,我們把它放在了表單的外圍上面,它將被按照下面的HTML和CSS格式渲染:
<ul class="errorlist nonfield">
<li>Generic validation error</li>
</ul>
4.迴圈表單的欄位:
如果你的表單欄位有相同的格式的HTML表現,那麼可以完全迴圈生成,不必要手動編寫每個欄位,減少冗餘的重複程式碼,只需要使用模板語言中的{% for %}
迴圈,如下所示:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
下面是{{ field }}
中非常有用的屬性,這些都是Django內建的模板語言給我們提供的方便:
屬性 | 說明 |
---|---|
{{ field.label}} | 欄位對應的label屬性 |
{{ field.label_tag }} | 自動生成欄位的label標籤,注意與{{ field.label }} 的區別 |
{{ field.id_for_label }} | 自定義欄位標籤的id |
{{ field.value}} | 當前欄位的值,比如一個Email欄位的值[email protected] |
{{ field.html_name }} | 指定欄位生成的input標籤中name屬性的值 |
{{ field.help_text }} | 欄位的幫助屬性 |
{{ field.errors }} | 包含錯誤資訊的元素 |
{{ field.is_hidden }} | 用於判斷當前欄位是否為隱藏的欄位,如果是,返回True |
{{ field.field }} | 返回欄位的引數列表。例如{{ char_field.field.max_length }} |
5.不可見欄位的特殊處理:
很多時候,我們的表單中會有一些隱藏的不可見欄位,比如honeypot。我們需要讓它在任何時候都彷彿不存在一般,比如錯誤的時候,如果你的頁面上顯示了不可見欄位的錯誤資訊,那麼使用者會很迷惑,這是哪來的呢?所以,通常,我們是不顯示不可見欄位的錯誤資訊的。
Django提供了兩種獨立的方法,用於迴圈那些不可和可見欄位,hidden_fields()
和visible_fields()
。這裡,我們可以稍稍修改一下前面的例子:
{# 迴圈那些不可見的欄位 #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{# 迴圈可見的欄位 #}
{% for field in form.visible_fields %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
6.重用表單模板
如果你在自己的HTML檔案中,多次使用同一個表單模板,那麼你完全可以把表單模板存成一個獨立的HTML檔案,然後在別的HTML檔案中通過include模板語法將其包含進來,如下例所示:
{% include "form_snippet.html" %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
如果你的頁面同時引用了好幾個不同的表單模板,那麼為了防止衝突,你可以使用with引數,給每個表單模板取個別名,如下所示:
{% include "form_snippet.html" with form=comment_form %}
在使用的是候就是:
{% for field in comment_form %}
......