表單 API¶
關於此文件
此文件涵蓋 Django 表單 API 的詳細資訊。您應該先閱讀表單使用入門。
繫結與未繫結表單¶
Form
實例可以是繫結到一組資料,或是未繫結。
如果它是繫結到一組資料,它可以驗證該資料,並將表單呈現為 HTML,並且在 HTML 中顯示資料。
如果它是未繫結,則無法進行驗證(因為沒有資料可以驗證!),但仍然可以將空白表單呈現為 HTML。
要建立未繫結的 Form
實例,請實例化類別
>>> f = ContactForm()
要將資料繫結到表單,請將資料作為字典,當作 Form
類別建構子的第一個參數傳遞
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
在此字典中,鍵是欄位名稱,對應於 Form
類別中的屬性。值是您嘗試驗證的資料。這些通常會是字串,但並非必須是字串;您傳遞的資料類型取決於Field
,我們稍後會看到。
- Form.is_bound¶
如果您需要在執行時區分繫結和未繫結的表單實例,請檢查表單的 is_bound
屬性的值
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({"subject": "hello"})
>>> f.is_bound
True
請注意,傳遞空的字典會建立具有空資料的繫結表單
>>> f = ContactForm({})
>>> f.is_bound
True
如果您有一個繫結的 Form
實例,並且想要以某種方式更改資料,或者您想要將未繫結的 Form
實例繫結到某些資料,請建立另一個 Form
實例。沒有方法可以更改 Form
實例中的資料。一旦建立 Form
實例,您應該將其資料視為不可變的,無論它是否有資料。
使用表單驗證資料¶
- Form.clean()¶
當您必須為相互依賴的欄位新增自訂驗證時,請在您的 Form
上實作 clean()
方法。請參閱清理和驗證彼此相依的欄位,以取得範例用法。
- Form.is_valid()¶
Form
物件的主要任務是驗證資料。對於繫結的 Form
實例,請呼叫 is_valid()
方法來執行驗證,並傳回一個布林值,指出資料是否有效
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
讓我們嘗試一些無效的資料。在此情況下,subject
是空白的(錯誤,因為預設情況下所有欄位都是必填的),並且 sender
不是有效的電子郵件地址
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
- Form.errors¶
存取 errors
屬性來取得錯誤訊息的字典
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
在此字典中,鍵是欄位名稱,值是代表錯誤訊息的字串列表。錯誤訊息儲存在列表中,因為一個欄位可以有多個錯誤訊息。
您可以存取 errors
,而無需先呼叫 is_valid()
。當您第一次呼叫 is_valid()
或存取 errors
時,將會驗證表單的資料。
無論您存取 errors
或呼叫 is_valid()
的次數多少,驗證常式都只會被呼叫一次。這表示如果驗證有副作用,這些副作用只會觸發一次。
- Form.errors.as_data()¶
傳回一個 dict
,將欄位對應到其原始的 ValidationError
實例。
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
當您需要依其 code
識別錯誤時,請隨時使用此方法。這可以啟用諸如重新撰寫錯誤訊息或在出現給定錯誤時在檢視中編寫自訂邏輯之類的操作。它也可以用來自訂格式(例如 XML)序列化錯誤;例如,as_json()
依賴於 as_data()
。
需要 as_data()
方法是因為回溯相容性。先前,當其呈現的錯誤訊息被新增到 Form.errors
字典時,ValidationError
實例會遺失。理想情況下,Form.errors
會儲存 ValidationError
實例,並且具有 as_
前綴的方法可以呈現它們,但為了不破壞預期 Form.errors
中呈現錯誤訊息的程式碼,必須以其他方式進行。
- Form.errors.as_json(escape_html=False)¶
傳回序列化為 JSON 的錯誤。
>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}
預設情況下,as_json()
不會逸出其輸出。如果您將其用於像是 AJAX 請求表單視圖,而客戶端會解析回應並將錯誤插入頁面的情況,您需要確保在客戶端逸出結果,以避免跨站腳本攻擊的可能性。您可以在 JavaScript 中使用 element.textContent = errorText
或使用 jQuery 的 $(el).text(errorText)
(而不是它的 .html()
函式) 來完成此操作。
如果因為某些原因您不想使用客戶端逸出,您也可以設定 escape_html=True
,這樣錯誤訊息將會被逸出,您就可以直接在 HTML 中使用它們。
- Form.errors.get_json_data(escape_html=False)¶
以適用於序列化為 JSON 的字典形式回傳錯誤。Form.errors.as_json()
回傳序列化後的 JSON,而此方法回傳的是序列化之前的錯誤資料。
escape_html
參數的行為如同在 Form.errors.as_json()
中描述的行為。
- Form.add_error(field, error)¶
此方法允許從 Form.clean()
方法內部,或從表單外部(例如,從視圖)將錯誤新增至特定的欄位。
field
引數是要將錯誤新增至的欄位名稱。如果其值為 None
,則該錯誤將被視為非欄位錯誤,如同由 Form.non_field_errors()
所回傳的錯誤。
error
引數可以是字串,或者最好是 ValidationError
的實例。有關定義表單錯誤的最佳實踐,請參閱 引發 ValidationError。
請注意,Form.add_error()
會自動從 cleaned_data
中移除相關欄位。
- Form.has_error(field, code=None)¶
此方法回傳一個布林值,表示某個欄位是否具有特定錯誤 code
的錯誤。如果 code
為 None
,如果該欄位包含任何錯誤,則回傳 True
。
若要檢查非欄位錯誤,請使用 NON_FIELD_ERRORS
作為 field
參數。
- Form.non_field_errors()¶
此方法回傳來自 Form.errors
的錯誤列表,這些錯誤與特定欄位無關。這包括在 Form.clean()
中引發的 ValidationError
以及使用 Form.add_error(None, "...")
新增的錯誤。
未綁定表單的行為¶
驗證沒有資料的表單是沒有意義的,但為了記錄,以下是未綁定表單會發生的情況
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
初始表單值¶
- Form.initial¶
使用 initial
來宣告表單欄位在執行階段的初始值。例如,您可能想要使用目前工作階段的使用者名稱填寫 username
欄位。
為了達成此目的,請使用 initial
引數到 Form
。如果提供了此引數,它應該是一個字典,將欄位名稱對應到初始值。只包含您要指定初始值的欄位;沒有必要在表單中包含每個欄位。例如
>>> f = ContactForm(initial={"subject": "Hi there!"})
這些值僅會顯示在未綁定的表單中,而且如果沒有提供特定值,它們不會被用作回退值。
如果 Field
定義了 initial
並且在建立 Form
實例時包含了 initial
,則後者的 initial
將具有優先權。在此範例中,initial
在欄位層級和表單實例層級都提供了,而後者具有優先權
>>> from django import forms
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial="class")
... url = forms.URLField()
... comment = forms.CharField()
...
>>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" value="instance" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
- Form.get_initial_for_field(field, field_name)¶
回傳表單欄位的初始資料。如果存在,它會從 Form.initial
中擷取資料,否則會嘗試 Field.initial
。可呼叫的值會被評估。
建議使用 BoundField.initial
而不是 get_initial_for_field()
,因為 BoundField.initial
具有更簡單的介面。此外,與 get_initial_for_field()
不同,BoundField.initial
會快取其值。當處理回傳值可能變化的可呼叫物件時,這特別有用(例如 datetime.now
或 uuid.uuid4
)
>>> import uuid
>>> class UUIDCommentForm(CommentForm):
... identifier = forms.UUIDField(initial=uuid.uuid4)
...
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
檢查哪些表單資料已變更¶
- Form.has_changed()¶
當您需要檢查表單資料是否從初始資料變更時,請在您的 Form
上使用 has_changed()
方法。
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False
當提交表單時,我們會重建它並提供原始資料,以便可以進行比較
>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()
如果 request.POST
中的資料與 initial
中提供的資料不同,has_changed()
將會是 True
,否則為 False
。結果是透過對表單中的每個欄位呼叫 Field.has_changed()
來計算的。
- Form.changed_data¶
changed_data
屬性會回傳一個列表,其中包含表單綁定資料(通常是 request.POST
)中的值與 initial
中提供的資料不同的欄位名稱。如果沒有資料不同,則回傳一個空列表。
>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
... print("The following fields changed: %s" % ", ".join(f.changed_data))
...
>>> f.changed_data
['subject', 'message']
從表單存取欄位¶
- Form.fields¶
您可以從 Form
實例的 fields
屬性中存取該實例的欄位
>>> for row in f.fields.values():
... print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields["name"]
<django.forms.fields.CharField object at 0x7ffaac6324d0>
您可以變更 Form
實例的欄位和 BoundField
,以變更其在表單中的呈現方式
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
>>> f["subject"].label = "Topic"
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Topic:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
請注意,不要更改 base_fields
屬性,因為此修改會影響同一 Python 進程中的所有後續 ContactForm
實例。
>>> f.base_fields["subject"].label_suffix = "?"
>>> another_f = ContactForm(auto_id=False)
>>> another_f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
存取「清理過」的資料¶
- Form.cleaned_data¶
Form
類別中的每個欄位不僅負責驗證資料,還負責「清理」資料,將其標準化為一致的格式。這是一個很好的功能,因為它允許以多種方式輸入特定欄位的資料,始終產生一致的輸出。
例如,DateField
將輸入標準化為 Python 的 datetime.date
物件。無論您傳遞的格式是 '1994-07-15'
字串、datetime.date
物件,還是其他多種格式,只要有效,DateField
都會將其標準化為 datetime.date
物件。
一旦您建立了一個具有一組資料並驗證過的 Form
實例,您就可以透過其 cleaned_data
屬性存取清理過的資料。
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
請注意,任何基於文字的欄位(例如 CharField
或 EmailField
)總是將輸入清理為字串。我們將在本文件中稍後討論編碼的含義。
如果您的資料未通過驗證,cleaned_data
字典只會包含有效的欄位。
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}
cleaned_data
將始終只包含在 Form
中定義的欄位的鍵,即使您在定義 Form
時傳遞了額外的資料。在此範例中,我們向 ContactForm
建構函式傳遞了一堆額外的欄位,但 cleaned_data
僅包含表單的欄位。
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... "extra_field_1": "foo",
... "extra_field_2": "bar",
... "extra_field_3": "baz",
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
當 Form
有效時,cleaned_data
將包含其所有欄位的鍵和值,即使資料中沒有某些可選欄位的值。在此範例中,資料字典不包含 nick_name
欄位的值,但 cleaned_data
包含它,且值為空。
>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
... nick_name = forms.CharField(required=False)
...
>>> data = {"first_name": "John", "last_name": "Lennon"}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}
在上面的範例中,nick_name
的 cleaned_data
值設定為空字串,因為 nick_name
是 CharField
,而 CharField
將空值視為空字串。每個欄位類型都知道其「空白」值是什麼 – 例如,對於 DateField
,它是 None
而不是空字串。有關每個欄位在此情況下的行為的完整詳細資訊,請參閱以下「內建 Field
類別」部分中每個欄位的「空值」註解。
您可以編寫程式碼來執行特定表單欄位(基於其名稱)或整個表單(考慮各種欄位的組合)的驗證。有關此的更多資訊,請參閱表單和欄位驗證。
將表單輸出為 HTML¶
Form
物件的第二個任務是以 HTML 格式呈現自身。要執行此操作,請 print
它。
>>> f = ContactForm()
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself"></div>
如果表單綁定到資料,則 HTML 輸出將適當地包含該資料。例如,如果一個欄位由 <input type="text">
表示,則資料將位於 value
屬性中。如果一個欄位由 <input type="checkbox">
表示,則該 HTML 將在適當情況下包含 checked
。
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" value="hello" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" value="Hi there" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" value="foo@example.com" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></div>
此預設輸出會用 <div>
包裹每個欄位。請注意以下事項:
為了靈活性,輸出中不包含
<form>
和</form>
標籤或<input type="submit">
標籤。這是您的工作。每個欄位類型都有一個預設的 HTML 表示形式。
CharField
由<input type="text">
表示,而EmailField
由<input type="email">
表示。BooleanField(null=False)
由<input type="checkbox">
表示。請注意,這些只是合理的預設值;您可以透過使用小部件來指定給定欄位要使用的 HTML,我們稍後會解釋。每個標籤的 HTML
name
直接取自其在ContactForm
類別中的屬性名稱。每個欄位的文字標籤,例如
'Subject:'
、'Message:'
和'Cc myself:'
是透過將所有底線轉換為空格並將第一個字母大寫,從欄位名稱產生的。同樣,請注意這些只是合理的預設值;您也可以手動指定標籤。每個文字標籤都包含在 HTML
<label>
標籤中,該標籤透過其id
指向適當的表單欄位。其id
反過來是透過在欄位名稱前面加上'id_'
而產生。預設情況下,輸出中包含id
屬性和<label>
標籤,以遵循最佳實務,但您可以變更該行為。輸出使用 HTML5 語法,目標是
<!DOCTYPE html>
。例如,它使用布林屬性(例如checked
),而不是 XHTML 樣式的checked='checked'
。
雖然當您 print
表單時,<div>
輸出是預設的輸出樣式,但您可以使用自己的表單範本自訂輸出,該範本可以在網站範圍、每個表單或每個實例中設定。請參閱可重複使用的表單範本。
預設呈現¶
當您 print
表單時,預設的呈現使用以下方法和屬性。
template_name
¶
- Form.template_name¶
如果將表單轉換為字串(例如透過 print(form)
或在範本中透過 {{ form }}
),則呈現的範本名稱。
預設情況下,一個屬性會傳回渲染器的 form_template_name
的值。您可以將其設定為字串範本名稱,以便覆寫特定表單類別的該屬性。
render()
¶
- Form.render(template_name=None, context=None, renderer=None)¶
render 方法由 __str__
以及 Form.as_div()
、Form.as_table()
、Form.as_p()
和 Form.as_ul()
方法呼叫。所有引數都是可選的,預設為
template_name
:Form.template_name
context
:Form.get_context()
傳回的值renderer
:由Form.default_renderer
回傳的值
透過傳遞 template_name
,您可以自訂僅用於單次呼叫的範本。
get_context()
¶
- Form.get_context()¶
回傳用於呈現表單的範本內容。
可用的內容如下:
form
:已綁定的表單。fields
:所有已綁定的欄位,隱藏欄位除外。hidden_fields
:所有隱藏的已綁定欄位。errors
:所有非欄位相關或隱藏欄位相關的表單錯誤。
template_name_label
¶
- Form.template_name_label¶
用於呈現欄位 <label>
的範本,在呼叫 BoundField.label_tag()
/legend_tag()
時使用。可以透過覆寫此屬性來針對每個表單變更,或者更一般地透過覆寫預設範本來變更,另請參閱 覆寫內建的表單範本。
輸出樣式¶
變更表單輸出樣式的建議方法是設定自訂表單範本,可以是全站範圍、每個表單或每個實例。請參閱 可重複使用的表單範本 以取得範例。
提供下列輔助函式以實現向後相容性,並且是傳遞特定 template_name
值的 Form.render()
的代理。
注意
在框架提供的範本和輸出樣式中,建議使用預設的 as_div()
,而不是 as_p()
、as_table()
和 as_ul()
版本,因為範本會實作 <fieldset>
和 <legend>
來分組相關輸入,且更容易讓螢幕閱讀器使用者瀏覽。
每個輔助函式都會將表單方法與提供適當範本名稱的屬性配對。
as_div()
¶
- Form.template_name_div¶
as_div()
使用的範本。預設值:'django/forms/div.html'
。
- Form.as_div()¶
as_div()
將表單呈現為一系列 <div>
元素,每個 <div>
包含一個欄位,例如
>>> f = ContactForm()
>>> f.as_div()
… 會產生類似以下的 HTML
<div>
<label for="id_subject">Subject:</label>
<input type="text" name="subject" maxlength="100" required id="id_subject">
</div>
<div>
<label for="id_message">Message:</label>
<input type="text" name="message" required id="id_message">
</div>
<div>
<label for="id_sender">Sender:</label>
<input type="email" name="sender" required id="id_sender">
</div>
<div>
<label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
</div>
as_p()
¶
- Form.template_name_p¶
as_p()
使用的範本。預設值:'django/forms/p.html'
。
- Form.as_p()¶
as_p()
將表單呈現為一系列 <p>
標籤,每個 <p>
包含一個欄位
>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.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> <input type="text" name="message" id="id_message" required></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>
as_ul()
¶
- Form.template_name_ul¶
as_ul()
使用的範本。預設值:'django/forms/ul.html'
。
- Form.as_ul()¶
as_ul()
將表單呈現為一系列 <li>
標籤,每個 <li>
包含一個欄位。它不包含 <ul>
或 </ul>
,以便您可以在 <ul>
上指定任何 HTML 屬性以實現彈性。
>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
as_table()
¶
- Form.template_name_table¶
as_table()
使用的範本。預設值:'django/forms/table.html'
。
- Form.as_table()¶
as_table()
將表單呈現為 HTML <table>
>>> f = ContactForm()
>>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f.as_table())
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>
設定必要或錯誤表單列的樣式¶
- Form.error_css_class¶
- Form.required_css_class¶
設定必要或有錯誤的表單列和欄位的樣式是很常見的。例如,您可能想要以粗體呈現必要的表單列,並以紅色醒目提示錯誤。
Form
類別有一些勾點,可用於將 class
屬性新增至必要的列或有錯誤的列:設定 Form.error_css_class
和/或 Form.required_css_class
屬性
from django import forms
class ContactForm(forms.Form):
error_css_class = "error"
required_css_class = "required"
# ... and the rest of your fields here
完成後,將會根據需要為列提供 "error"
和/或 "required"
類別。HTML 看起來會像這樣
>>> f = ContactForm(data)
>>> print(f)
<div class="required"><label for="id_subject" class="required">Subject:</label> ...
<div class="required"><label for="id_message" class="required">Message:</label> ...
<div class="required"><label for="id_sender" class="required">Sender:</label> ...
<div><label for="id_cc_myself">Cc myself:</label> ...
>>> f["subject"].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f["subject"].legend_tag()
<legend class="required" for="id_subject">Subject:</legend>
>>> f["subject"].label_tag(attrs={"class": "foo"})
<label for="id_subject" class="foo required">Subject:</label>
>>> f["subject"].legend_tag(attrs={"class": "foo"})
<legend for="id_subject" class="foo required">Subject:</legend>
設定表單小工具的呈現方式¶
- Form.default_renderer¶
指定用於表單的渲染器。預設值為 None
,這表示使用 FORM_RENDERER
設定所指定的預設渲染器。
您可以在宣告表單時將其設定為類別屬性,或使用 Form.__init__()
的 renderer
引數。例如
from django import forms
class MyForm(forms.Form):
default_renderer = MyRenderer()
或
form = MyForm(renderer=MyRenderer())
關於欄位順序的注意事項¶
在 as_p()
、as_ul()
和 as_table()
快捷方式中,欄位會按照您在表單類別中定義它們的順序顯示。例如,在 ContactForm
範例中,欄位會按照 subject
、message
、sender
、cc_myself
的順序定義。若要重新排列 HTML 輸出,請變更這些欄位在類別中列出的順序。
還有其他幾種方法可以自訂順序
- Form.field_order¶
預設情況下,Form.field_order=None
,這會保留您在表單類別中定義欄位的順序。如果 field_order
是欄位名稱的清單,則欄位會按照清單指定的順序排列,其餘欄位會根據預設順序附加。清單中未知的欄位名稱會被忽略。這使得可以通過將其設定為 None
來停用子類別中的欄位,而無需重新定義順序。
您也可以使用 Form
的 Form.field_order
引數來覆寫欄位順序。如果 Form
定義了 field_order
並且您在實例化 Form
時包含 field_order
,則後者的 field_order
將具有優先權。
- Form.order_fields(field_order)¶
您可以隨時使用 order_fields()
重新排列欄位,並使用欄位名稱清單,如 field_order
中所示。
錯誤的顯示方式¶
如果您呈現已繫結的 Form
物件,則呈現的動作會自動執行表單的驗證(如果尚未發生),並且 HTML 輸出將在欄位附近包含驗證錯誤,作為 <ul class="errorlist">
。錯誤訊息的特定位置取決於您使用的輸出方法。
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data, auto_id=False)
>>> print(f)
<div>Subject:
<ul class="errorlist"><li>This field is required.</li></ul>
<input type="text" name="subject" maxlength="100" required aria-invalid="true">
</div>
<div>Message:
<textarea name="message" cols="40" rows="10" required>Hi there</textarea>
</div>
<div>Sender:
<ul class="errorlist"><li>Enter a valid email address.</li></ul>
<input type="email" name="sender" value="invalid email address" required aria-invalid="true">
</div>
<div>Cc myself:
<input type="checkbox" name="cc_myself" checked>
</div>
自訂錯誤清單格式¶
- class ErrorList(initlist=None, error_class=None, renderer=None)[原始碼]¶
預設情況下,表單會使用
django.forms.utils.ErrorList
來格式化驗證錯誤。ErrorList
是一個類似清單的物件,其中initlist
是錯誤的清單。此外,此類別還具有下列屬性和方法。- error_class¶
呈現錯誤清單時要使用的 CSS 類別。任何提供的類別都會新增至預設的
errorlist
類別。
- renderer¶
指定用於
ErrorList
的渲染器。預設值為None
,這表示使用FORM_RENDERER
設定所指定的預設渲染器。
- template_name¶
當呼叫
__str__
或render()
時所使用的範本名稱。預設值為'django/forms/errors/list/default.html'
,它是'ul.html'
範本的代理。
- template_name_text¶
當呼叫
as_text()
時所使用的範本名稱。預設值為'django/forms/errors/list/text.html'
。此範本會將錯誤呈現為項目符號清單。
- template_name_ul¶
當呼叫
as_ul()
時所使用的範本名稱。預設值為'django/forms/errors/list/ul.html'
。此範本會將錯誤呈現在<li>
標籤中,並使用由error_class
定義的 CSS 類別包裹在<ul>
中。
- render(template_name=None, context=None, renderer=None)¶
render
方法會被__str__
以及as_ul()
方法呼叫。所有參數都是可選的,預設值如下:
template_name
:由template_name
傳回的值context
:由get_context()
傳回的值renderer
:由renderer
傳回的值
- as_text()¶
使用由
template_name_text
定義的範本呈現錯誤列表。
- as_ul()¶
使用由
template_name_ul
定義的範本呈現錯誤列表。
如果您想自訂錯誤的呈現方式,可以覆寫
template_name
屬性,或者更普遍地說,覆寫預設範本,另請參閱 覆寫內建表單範本。
更精細的輸出¶
as_p()
、as_ul()
和 as_table()
方法是快捷方式,它們並非顯示表單物件的唯一方式。
若要檢索單一的 BoundField
,請使用表單上的字典查找語法,並以欄位的名稱作為鍵。
>>> form = ContactForm()
>>> print(form["subject"])
<input id="id_subject" type="text" name="subject" maxlength="100" required>
若要檢索所有 BoundField
物件,請迭代表單。
>>> form = ContactForm()
>>> for boundfield in form:
... print(boundfield)
...
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
欄位特定的輸出會遵循表單物件的 auto_id
設定。
>>> f = ContactForm(auto_id=False)
>>> print(f["message"])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id="id_%s")
>>> print(f["message"])
<input type="text" name="message" id="id_message" required>
BoundField
的屬性¶
- BoundField.auto_id[原始碼]¶
此
BoundField
的 HTML ID 屬性。如果Form.auto_id
為False
,則傳回空字串。
- BoundField.data[原始碼]¶
此屬性會傳回此
BoundField
的資料,這些資料由 widget 的value_from_datadict()
方法擷取,如果未提供則為None
。>>> unbound_form = ContactForm() >>> print(unbound_form["subject"].data) None >>> bound_form = ContactForm(data={"subject": "My Subject"}) >>> print(bound_form["subject"].data) My Subject
- BoundField.errors[原始碼]¶
一個 類列表物件,在列印時會顯示為 HTML
<ul class="errorlist">
。>>> data = {"subject": "hi", "message": "", "sender": "", "cc_myself": ""} >>> f = ContactForm(data, auto_id=False) >>> print(f["message"]) <input type="text" name="message" required aria-invalid="true"> >>> f["message"].errors ['This field is required.'] >>> print(f["message"].errors) <ul class="errorlist"><li>This field is required.</li></ul> >>> f["subject"].errors [] >>> print(f["subject"].errors) >>> str(f["subject"].errors) ''
當呈現有錯誤的欄位時,會在欄位的 widget 上設定
aria-invalid="true"
,以指示螢幕閱讀器使用者有錯誤。在 Django 5.0 中變更當欄位有錯誤時,會新增
aria-invalid="true"
。
- BoundField.field¶
此
BoundField
包裝的表單Field
實例。
- BoundField.form¶
此
BoundField
所綁定的Form
實例。
- BoundField.id_for_label[原始碼]¶
使用此屬性來呈現此欄位的 ID。例如,如果您正在您的範本中手動建構
<label>
(儘管label_tag()
/legend_tag()
會為您執行此操作)<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
預設情況下,這會是欄位的名稱加上
id_
前綴(以上範例為「id_my_field
」)。您可以透過在欄位的 widget 上設定attrs
來修改 ID。例如,像這樣宣告一個欄位my_field = forms.CharField(widget=forms.TextInput(attrs={"id": "myFIELD"}))
並使用上述範本,會呈現類似如下的內容
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
- BoundField.initial[原始碼]¶
使用
BoundField.initial
來檢索表單欄位的初始資料。它會從Form.initial
中檢索資料(如果存在),否則會嘗試Field.initial
。可呼叫的值會被評估。請參閱 初始表單值 以取得更多範例。BoundField.initial
會快取其回傳值,這在處理回傳值可能會變更的可呼叫物件(例如datetime.now
或uuid.uuid4
)時特別有用。>>> from datetime import datetime >>> class DatedCommentForm(CommentForm): ... created = forms.DateTimeField(initial=datetime.now) ... >>> f = DatedCommentForm() >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54) >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54)
建議使用
BoundField.initial
而不是get_initial_for_field()
。
如果此
BoundField
的 widget 是隱藏的,則回傳True
。
- BoundField.label¶
欄位的
label
。這用於label_tag()
/legend_tag()
中。
- BoundField.name¶
此欄位在表單中的名稱
>>> f = ContactForm() >>> print(f["subject"].name) subject >>> print(f["message"].name) message
- BoundField.template_name[原始碼]¶
- 在 Django 5.0 中新增。
使用
BoundField.as_field_group()
呈現的範本名稱。一個屬性,如果設定了
template_name
,則傳回其值,否則傳回field_template_name
。
BoundField
的方法¶
- BoundField.as_field_group()¶
- 在 Django 5.0 中新增。
使用
BoundField.render()
和預設值來呈現欄位,此方法會使用範本的template_name
(如果已設定)或field_template_name
來呈現BoundField
,包括其標籤、說明文字和錯誤。
傳回 HTML 字串,將此呈現為
<input type="hidden">
。**kwargs
會傳遞至as_widget()
。此方法主要在內部使用。您應該改用 widget。
- BoundField.as_widget(widget=None, attrs=None, only_initial=False)[原始碼]¶
透過呈現傳遞的 widget 來呈現欄位,並新增任何以
attrs
傳遞的 HTML 屬性。如果未指定 widget,則會使用欄位的預設 widget。only_initial
由 Django 內部使用,不應明確設定。
- BoundField.css_classes(extra_classes=None)[原始碼]¶
當您使用 Django 的呈現捷徑時,CSS 類別會用於指示必要的表單欄位或包含錯誤的欄位。如果您正在手動呈現表單,則可以使用
css_classes
方法來存取這些 CSS 類別。>>> f = ContactForm(data={"message": ""}) >>> f["message"].css_classes() 'required'
如果您想要在可能需要的錯誤和必要類別之外提供一些額外的類別,您可以將這些類別作為引數提供
>>> f = ContactForm(data={"message": ""}) >>> f["message"].css_classes("foo bar") 'foo bar required'
- BoundField.label_tag(contents=None, attrs=None, label_suffix=None, tag=None)[原始碼]¶
使用
Form.template_name_label
指定的模板,為表單欄位渲染標籤。可用的內容如下:
field
:這是BoundField
的實例。contents
:預設為BoundField.label
和Form.label_suffix
(或Field.label_suffix
,如果已設定) 的串連字串。這可以使用contents
和label_suffix
參數覆寫。attrs
:一個包含for
、Form.required_css_class
和id
的dict
。id
由欄位的 widgetattrs
或BoundField.auto_id
生成。其他屬性可以使用attrs
參數提供。use_tag
:一個布林值,如果標籤有id
,則為True
。如果為False
,則預設模板會省略tag
。tag
:一個可選的字串,用於自訂標籤,預設為label
。
提示
在您的模板中,
field
是BoundField
的實例。因此,field.field
會存取BoundField.field
,這是您宣告的欄位,例如forms.CharField
。若要分別渲染表單欄位的標籤,您可以呼叫其
label_tag()
方法>>> f = ContactForm(data={"message": ""}) >>> print(f["message"].label_tag()) <label for="id_message">Message:</label>
如果您想自訂渲染,可以覆寫
Form.template_name_label
屬性,或更一般地覆寫預設模板,另請參閱 覆寫內建表單範本。
- BoundField.legend_tag(contents=None, attrs=None, label_suffix=None)[原始碼]¶
使用
tag='legend'
呼叫label_tag()
,以使用<legend>
標籤渲染標籤。當渲染單選和多個核取方塊 widget 時,<legend>
可能比<label>
更適合,這會很有用。
- BoundField.render(template_name=None, context=None, renderer=None)¶
- 在 Django 5.0 中新增。
render 方法由
as_field_group
呼叫。所有參數都是可選的,預設值為template_name
:BoundField.template_name
context
:由BoundField.get_context()
傳回的值renderer
:由Form.default_renderer
回傳的值
透過傳遞
template_name
,您可以自訂僅用於單次呼叫的範本。
- BoundField.value()[原始碼]¶
使用這個方法來渲染這個欄位的原始值,就像它會被
Widget
渲染一樣>>> initial = {"subject": "welcome"} >>> unbound_form = ContactForm(initial=initial) >>> bound_form = ContactForm(data={"subject": "hi"}, initial=initial) >>> print(unbound_form["subject"].value()) welcome >>> print(bound_form["subject"].value()) hi
自訂 BoundField
¶
如果您需要在模板中存取有關表單欄位的一些其他資訊,並且使用 Field
的子類別不足以滿足您的需求,請考慮自訂 BoundField
。
自訂表單欄位可以覆寫 get_bound_field()
- Field.get_bound_field(form, field_name)[原始碼]¶
接受
Form
的實例和欄位的名稱。當在模板中存取欄位時,將會使用傳回值。最有可能的情況是,它會是BoundField
子類別的實例。
例如,如果您有一個 GPSCoordinatesField
,並且想要在模板中存取有關座標的其他資訊,可以如下方式實作
class GPSCoordinatesBoundField(BoundField):
@property
def country(self):
"""
Return the country the coordinates lie in or None if it can't be
determined.
"""
value = self.value()
if value:
return get_country_from_coordinates(value)
else:
return None
class GPSCoordinatesField(Field):
def get_bound_field(self, form, field_name):
return GPSCoordinatesBoundField(form, self, field_name)
現在您可以使用 {{ form.coordinates.country }}
在模板中存取國家/地區。
將上傳的檔案繫結到表單¶
處理具有 FileField
和 ImageField
欄位的表單比一般表單稍微複雜一些。
首先,為了上傳檔案,您需要確保您的 <form>
元素正確地將 enctype
定義為 "multipart/form-data"
<form enctype="multipart/form-data" method="post" action="/foo/">
其次,當您使用表單時,需要繫結檔案資料。檔案資料的處理方式與一般表單資料不同,因此當您的表單包含 FileField
和 ImageField
時,您需要在繫結表單時指定第二個引數。因此,如果我們擴充 ContactForm 以包含一個名為 mugshot
的 ImageField
,我們需要繫結包含 mugshot 影像的檔案資料
# Bound form with an image field
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> file_data = {"mugshot": SimpleUploadedFile("face.jpg", b"file data")}
>>> f = ContactFormWithMugshot(data, file_data)
實際上,您通常會指定 request.FILES
作為檔案資料的來源 (就像您使用 request.POST
作為表單資料的來源一樣)
# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)
建構未繫結的表單與以往相同 – 省略表單資料*和*檔案資料
# Unbound form with an image field
>>> f = ContactFormWithMugshot()
測試多部分表單¶
- Form.is_multipart()¶
如果您正在編寫可重複使用的檢視或範本,您可能無法事先知道您的表單是否為多部分表單。is_multipart()
方法會告訴您表單是否需要多部分編碼以進行提交
>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True
以下是在範本中如何使用此功能的範例
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>
子類別化表單¶
如果您有多個共用欄位的 Form
類別,您可以使用子類別化來消除冗餘。
當您子類別化自訂 Form
類別時,產生的子類別將包含父類別的所有欄位,後接您在子類別中定義的欄位。
在這個範例中,ContactFormWithPriority
包含 ContactForm
的所有欄位,外加一個額外的欄位 priority
。ContactForm
的欄位會先排序
>>> class ContactFormWithPriority(ContactForm):
... priority = forms.CharField()
...
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
<div>Priority:<input type="text" name="priority" required></div>
可以將多個表單子類化,將表單視為混入(mixins)。在此範例中,BeatleForm
子類化了 PersonForm
和 InstrumentForm
(依照此順序),並且其欄位列表包含來自父類別的欄位。
>>> from django import forms
>>> class PersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
...
>>> class InstrumentForm(forms.Form):
... instrument = forms.CharField()
...
>>> class BeatleForm(InstrumentForm, PersonForm):
... haircut_type = forms.CharField()
...
>>> b = BeatleForm(auto_id=False)
>>> print(b)
<div>First name:<input type="text" name="first_name" required></div>
<div>Last name:<input type="text" name="last_name" required></div>
<div>Instrument:<input type="text" name="instrument" required></div>
<div>Haircut type:<input type="text" name="haircut_type" required></div>
可以宣告式地移除從父類別繼承的 Field
,方法是在子類別中將該欄位的名稱設定為 None
。例如:
>>> from django import forms
>>> class ParentForm(forms.Form):
... name = forms.CharField()
... age = forms.IntegerField()
...
>>> class ChildForm(ParentForm):
... name = None
...
>>> list(ChildForm().fields)
['age']
表單的前綴¶
- Form.prefix¶
您可以在一個 <form>
標籤內放置多個 Django 表單。若要為每個 Form
提供各自的命名空間,請使用 prefix
關鍵字引數。
>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother)
<div><label for="id_mother-first_name">First name:</label><input type="text" name="mother-first_name" required id="id_mother-first_name"></div>
<div><label for="id_mother-last_name">Last name:</label><input type="text" name="mother-last_name" required id="id_mother-last_name"></div>
>>> print(father)
<div><label for="id_father-first_name">First name:</label><input type="text" name="father-first_name" required id="id_father-first_name"></div>
<div><label for="id_father-last_name">Last name:</label><input type="text" name="father-last_name" required id="id_father-last_name"></div>
前綴也可以在表單類別上指定。
>>> class PersonForm(forms.Form):
... ...
... prefix = "person"
...