# 表單 API
關于這篇文檔
這篇文檔講述Django 表單API 的詳細細節。你應該先閱讀[_表單簡介_](../../topics/forms/index.html)。
## 綁定的表單和未綁定的表單
[`表單`](#django.forms.Form "django.forms.Form")要么是**綁定的**,要么是**未綁定的**。
* 如果是**綁定的**,那么它能夠驗證數據,并渲染表單及其數據成HTML。
* 如果是**未綁定的**,那么它不能夠完成驗證(因為沒有可驗證的數據!),但是仍然能渲染空白的表單成HTML。
_class _`Form`
若要創建一個未綁定的[`表單`](#django.forms.Form "django.forms.Form")實例,只需簡單地實例化該類:
```
>>> f = ContactForm()
```
若要綁定數據到表單,可以將數據以字典的形式傳遞給[`表單`](#django.forms.Form "django.forms.Form")類的構造函數的第一個參數:
```
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
```
在這個字典中,鍵為字段的名稱,它們對應于[`表單`](#django.forms.Form "django.forms.Form")類中的屬性。值為需要驗證的數據。它們通常為字符串,但是沒有強制要求必須是字符串;傳遞的數據類型取決于[`字段`](fields.html#django.forms.Field "django.forms.Field"),我們稍后會看到。
`Form.``is_bound`
如果運行時刻你需要區分綁定的表單和未綁定的表單,可以檢查下表單[`is_bound`](#django.forms.Form.is_bound "django.forms.Form.is_bound") 屬性的值:
```
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True
```
注意,傳遞一個空的字典將創建一個帶有空數據的_綁定的_表單:
```
>>> f = ContactForm({})
>>> f.is_bound
True
```
如果你有一個綁定的[`表單`](#django.forms.Form "django.forms.Form")實例但是想改下數據,或者你想綁定一個未綁定的[`表單`](#django.forms.Form "django.forms.Form")表單到某些數據,你需要創建另外一個[`表單`](#django.forms.Form "django.forms.Form")實例。[`Form`](#django.forms.Form "django.forms.Form") 實例的數據沒有辦法修改。[`表單`](#django.forms.Form "django.forms.Form")實例一旦創建,你應該將它的數據視為不可變的,無論它有沒有數據。
## 使用表單來驗證數據
`Form.``clean`()
當你需要為相互依賴的字段添加自定義的驗證時,你可以實現`表單`的`clean()`方法。示例用法參見[_Cleaning and validating fields that depend on each other_](validation.html#validating-fields-with-clean)。
`Form.``is_valid`()
[`表單`](#django.forms.Form "django.forms.Form")對象的首要任務就是驗證數據。對于綁定的[`表單`](#django.forms.Form "django.forms.Form")實例,可以調用[`is_valid()`](#django.forms.Form.is_valid "django.forms.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`](#django.forms.Form.errors "django.forms.Form.errors") 屬性可以獲得錯誤信息的一個字典:
```
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
```
在這個字典中,鍵為字段的名稱,值為表示錯誤信息的Unicode 字符串組成的列表。錯誤信息保存在列表中是因為字段可能有多個錯誤信息。
你可以在調用[`is_valid()`](#django.forms.Form.is_valid "django.forms.Form.is_valid") 之前訪問[`errors`](#django.forms.Form.errors "django.forms.Form.errors")。表單的數據將在第一次調用[`is_valid()`](#django.forms.Form.is_valid "django.forms.Form.is_valid") 或者訪問[`errors`](#django.forms.Form.errors "django.forms.Form.errors") 時驗證。
驗證將值調用一次,無論你訪問[`errors`](#django.forms.Form.errors "django.forms.Form.errors") 或者調用[`is_valid()`](#django.forms.Form.is_valid "django.forms.Form.is_valid") 多少次。這意味著,如果驗證過程有副作用,這些副作用將只觸發一次。
`Form.errors.``as_data`()
New in Django 1.7\.
返回一個`字典`,它映射字段到原始的`ValidationError` 實例。
```
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
```
每當你需要根據錯誤的`code` 來識別錯誤時,可以調用這個方法。它可以用來重寫錯誤信息或者根據特定的錯誤編寫自定義的邏輯。它還可以用來序列化錯誤為一個自定義的格式(例如,XML);[`as_json()`](#django.forms.Form.errors.as_json "django.forms.Form.errors.as_json") 就依賴于`as_data()`。
需要`as_data()` 方法是為了向后兼容。以前,`ValidationError` 實例在它們**渲染后** 的錯誤消息一旦添加到`Form.errors` 字典就立即被丟棄。理想情況下,`Form.errors` 應該已經保存`ValidationError` 實例而帶有`as_` 前綴的方法可以渲染它們,但是為了不破壞直接使用`Form.errors` 中的錯誤消息的代碼,必須使用其它方法來實現。
`Form.errors.``as_json`(_escape_html=False_)
New in Django 1.7\.
返回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 庫比如jQuery 來做這件事很簡單 —— 只要使用`$(el).text(errorText)` 而不是`.html()` 就可以。
如果由于某種原因你不想使用客戶端的轉義,你還可以設置`escape_html=True`,這樣錯誤消息將被轉義而你可以直接在HTML 中使用它們。
`Form.``add_error`(_field_, _error_)
New in Django 1.7\.
這個方法允許在`Form.clean()` 方法內部或從表單的外部一起給字段添加錯誤信息;例如從一個視圖中。
`field` 參數為字段的名稱。如果值為`None`,error 將作為[`Form.non_field_errors()`](#django.forms.Form.non_field_errors "django.forms.Form.non_field_errors") 返回的一個非字段錯誤。
`error` 參數可以是一個簡單的字符串,或者最好是一個`ValidationError` 實例。[_引發ValidationError_](validation.html#raising-validation-error) 中可以看到定義表單錯誤時的最佳實踐。
注意,`Form.add_error()` 會自動刪除`cleaned_data` 中的相關字段。
`Form.``has_error`(_field_, _code=None_)
New in Django 1.8\.
這個方法返回一個布爾值,指示一個字段是否具有指定錯誤`code` 的錯誤。當`code` 為`None` 時,如果字段有任何錯誤它都將返回`True`。
若要檢查非字段錯誤,使用[`NON_FIELD_ERRORS`](../exceptions.html#django.core.exceptions.NON_FIELD_ERRORS "django.core.exceptions.NON_FIELD_ERRORS") 作為`field` 參數。
`Form.``non_field_errors`()
這個方法返回[`Form.errors`](#django.forms.Form.errors "django.forms.Form.errors") 中不是與特定字段相關聯的錯誤。它包含在[`Form.clean()`](#django.forms.Form.clean "django.forms.Form.clean") 中引發的`ValidationError` 和使用[`Form.add_error(None, "...")`](#django.forms.Form.add_error "django.forms.Form.add_error") 添加的錯誤。
### 未綁定表單的行為
驗證沒有綁定數據的表單是沒有意義的,下面的例子展示了這種情況:
```
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
```
## 動態的初始值
`Form.``initial`
表單字段的初始值使用[`initial`](#django.forms.Form.initial "django.forms.Form.initial")聲明。例如,你可能希望使用當前會話的用戶名填充`username`字段。
使用[`Form`](#django.forms.Form "django.forms.Form")的[`initial`](#django.forms.Form.initial "django.forms.Form.initial")參數可以實現。該參數是字段名到初始值的一個字典。只需要包含你期望給出初始值的字段;不需要包含表單中的所有字段。例如:
```
>>> f = ContactForm(initial={'subject': 'Hi there!'})
```
這些值只顯示在沒有綁定的表單中,即使沒有提供特定值它們也不會作為后備的值。
注意,如果[`字段`](fields.html#django.forms.Field "django.forms.Field")有定義[`initial`](#django.forms.Form.initial "django.forms.Form.initial"), _而_實例化`表單`時也提供`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)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
```
## 檢查表單數據是否改變
`Form.``has_changed`()
當你需要檢查表單的數據是否從初始數據發生改變時,可以使用`表單`的`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`](#django.forms.Form.initial "django.forms.Form.initial") 中的不同,`has_changed()` 將為`True`,否則為`False`。 計算的結果是通過調用表單每個字段的[`Field.has_changed()`](fields.html#django.forms.Field.has_changed "django.forms.Field.has_changed") 得到的。
## 從表單中訪問字段
`Form.``fields`
你可以從[`表單`](#django.forms.Form "django.forms.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>
```
可你可以修改[`表單`](#django.forms.Form "django.forms.Form")實例的字段來改變字段在表單中的表示:
```
>>> f.as_table().split('\n')[0]
'<tr><th>Name:</th><td><input name="name" type="text" value="instance" /></td></tr>'
>>> f.fields['name'].label = "Username"
>>> f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="instance" /></td></tr>'
```
注意不要改變`base_fields` 屬性,因為一旦修改將影響同一個Python 進程中接下來所有的`ContactForm` 實例:
```
>>> f.base_fields['name'].label = "Username"
>>> another_f = CommentForm(auto_id=False)
>>> another_f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="class" /></td></tr>'
```
## 訪問“清潔”的數據
`Form.``cleaned_data`
[`表單`](#django.forms.Form "django.forms.Form")類中的每個字段不僅負責驗證數據,還負責“清潔”它們 —— 將它們轉換為正確的格式。這是個非常好用的功能,因為它允許字段以多種方式輸入數據,并總能得到一致的輸出。
例如,[`DateField`](fields.html#django.forms.DateField "django.forms.DateField") 將輸入轉換為Python 的 `datetime.date` 對象。無論你傳遞的是`'1994-07-15'` 格式的字符串、`datetime.date` 對象、還是其它格式的數字,`DateField` 將始終將它們轉換成`datetime.date` 對象,只要它們是合法的。
一旦你創建一個[`表單`](#django.forms.Form "django.forms.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` —— 始終將輸入轉換為Unicode 字符串。我們將在這篇文檔的后面將是編碼的影響。
如果你的數據_沒有_ 通過驗證,`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` 始終_只_ 包含`表單`中定義的字段,即使你在構建`表單` 時傳遞了額外的數據。在下面的例子中,我們傳遞一組額外的字段給`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'}
```
當`表單`合法時,`cleaned_data` 將包含_所有_字段的鍵和值,即使傳遞的數據不包含某些可選字段的值。在下面的例子中,傳遞的數據字典不包含`nick_name` 字段的值,但是`cleaned_data` 任然包含它,只是值為空:
```
>>> from django.forms import Form
>>> class OptionalPersonForm(Form):
... first_name = CharField()
... last_name = CharField()
... nick_name = 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'}
```
在上面的例子中,`cleaned_data` 中`nick_name` 設置為一個空字符串,這是因為`nick_name` 是`CharField`而 `CharField` 將空值作為一個空字符串。每個字段都知道自己的“空”值 —— 例如,`DateField` 的空值是`None` 而不是一個空字符串。關于每個字段空值的完整細節,參見“內建的`Field` 類”一節中每個字段的“空值”提示。
你可以自己編寫代碼來對特定的字段(根據它們的名字)或者表單整體(考慮到不同字段的組合)進行驗證。更多信息參見[_表單和字段驗證_](validation.html)。
## 輸出表單為HTML
`表單`對象的第二個任務是將它渲染成HTML。很簡單,`print` 它:
```
>>> f = ContactForm()
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></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>
```
如果表單是綁定的,輸出的HTML 將包含數據。例如,如果字段是`<input type="text">` 的形式,其數據將位于`value` 屬性中。如果字段是`<input type="checkbox">` 的形式,HTML 將包含`checked="checked"`:
```
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
```
默認的輸出時具有兩個列的HTML 表格,每個字段對應一個`<tr>`。注意事項:
* 為了靈活性,輸出_不_包含`<table>` 和`</table>`、`<form>` 和`</form>` 以及`<input type="submit">` 標簽。你需要添加它們。
* 每個字段類型有一個默認的HTML 表示。`CharField` 表示為一個`<input type="text">`,`EmailField` 表示為一個`<input type="email">`。`BooleanField` 表示為一個`<input type="checkbox">`。注意,這些只是默認的表示;你可以使用Widget 指定字段使用哪種HTML,我們將稍后解釋。
* 每個標簽的HTML `name` 直接從`ContactForm` 類中獲取。
* 每個字段的文本標簽 —— 例如`'Subject:'`、`'Message:'` 和`'Cc myself:'` 通過將所有的下劃線轉換成空格并大寫第一個字母生成。再次提醒,這些只是默認的表示;你可以手工指定標簽。
* 每個文本標簽周圍有一個HTML `<label>` 標簽,它指向表單字段的`id`。這個`id`,是通過在字段名稱前面加上`'id_'` 前綴生成。`id` 屬性和`<label>` 標簽默認包含在輸出中,但你可以改變這一行為。
雖然`print` 表單時`<table>` 是默認的輸出格式,但是還有其它格式可用。每個格式對應于表單對象的一個方法,每個方法都返回一個Unicode 對象。
### as_p()
`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" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></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" /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
```
### as_ul()
`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" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></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" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
```
### as_table()
`Form.``as_table`()
最后,`as_table()`輸出表單為一個HTML `<table>`。它與`print` 完全相同。事實上,當你`print` 一個表單對象時,在后臺調用的就是`as_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" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></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" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></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`
將必填的表單行和有錯誤的表單行定義不同的樣式特別常見。例如,你想將必填的表單行以粗體顯示、將錯誤以紅色顯示。
[`表單`](#django.forms.Form "django.forms.Form")類具有一對鉤子,可以使用它們來添加`class` 屬性給必填的行或有錯誤的行:只需簡單地設置[`Form.error_css_class`](#django.forms.Form.error_css_class "django.forms.Form.error_css_class") 和/或 [`Form.required_css_class`](#django.forms.Form.required_css_class "django.forms.Form.required_css_class") 屬性:
```
from django.forms import Form
class ContactForm(Form):
error_css_class = 'error'
required_css_class = 'required'
# ... and the rest of your fields here
```
一旦你設置好,將根據需要設置行的`"error"` 和/或`"required"` CSS 類型。 其HTML 看上去將類似:
```
>>> f = ContactForm(data)
>>> print(f.as_table())
<tr class="required"><th><label class="required" for="id_subject">Subject:</label> ...
<tr class="required"><th><label class="required" for="id_message">Message:</label> ...
<tr class="required error"><th><label class="required" for="id_sender">Sender:</label> ...
<tr><th><label for="id_cc_myself">Cc myself:<label> ...
>>> f['subject'].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f['subject'].label_tag(attrs={'class': 'foo'})
<label for="id_subject" class="foo required">Subject:</label>
```
Changed in Django 1.8:
`required_css_class` 添加到`<label>` 標簽,如上面所看到的。
### 配置表單元素的HTML `id` 屬性和 `<label>` 標簽
`Form.``auto_id`
默認情況下,表單的渲染方法包含:
* 表單元素的HTML `id` 屬性
* 對應的`<label>` 標簽。HTML `<label>` 標簽指示標簽文本關聯的表單元素。這個小小的改進讓表單在輔助設備上具有更高的可用性。使用`<label>` 標簽始終是個好想法。
`id` 屬性值通過在表單字段名稱的前面加上`id_` 生成。但是如果你想改變`id` 的生成方式或者完全刪除 HTML `id` 屬性和`<label>`標簽,這個行為是可配置的。
`id` 和label 的行為使用`表單`構造函數的`auto_id` 參數控制。這個參數必須為`True`、`False` 或者一個字符串。
如果`auto_id` 為`False`,那么表單的輸出將不包含`<label>` 標簽和`id` 屬性:
```
>>> f = ContactForm(auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" /></td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="email" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" /></p>
<p>Sender: <input type="email" name="sender" /></p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
```
如果`auto_id` 設置為`True`,那么輸出的表示_將_ 包含`<label>` 標簽并簡單地使用字典名稱作為每個表單字段的`id`:
```
>>> f = ContactForm(auto_id=True)
>>> print(f.as_table())
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" /></td></tr>
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
<li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></li>
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
>>> print(f.as_p())
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
<p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
```
如果`auto_id` 設置為包含格式字符`'%s'` 的字符串,那么表單的輸出將包含`<label>` 標簽,并將根據格式字符串生成`id` 屬性。例如,對于格式字符串`'field_%s'`,名為`subject` 的字段的`id` 值將是`'field_subject'`。繼續我們的例子:
```
>>> f = ContactForm(auto_id='id_for_%s')
>>> print(f.as_table())
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="email" name="sender" id="id_for_sender" /></td></tr>
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> print(f.as_p())
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
<p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></p>
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
```
如果`auto_id` 設置為任何其它的真值 —— 例如不包含`%s` 的字符串 —— 那么其行為將類似`auto_id` 等于`True`。
默認情況下,`auto_id` 設置為`'id_%s'`。
`Form.``label_suffix`
一個字符串(默認為英文的`:`),表單渲染時將附加在每個label 名稱的后面。
使用`label_suffix` 參數可以自定義這個字符,或者完全刪除它:
```
>>> f = ContactForm(auto_id='id_for_%s', label_suffix='')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
```
注意,該標簽后綴只有當label 的最后一個字符不是表單符號(`.`, `!`, `?` 和`:`)時才添加。
New in Django 1.8\.
字段可以定義自己的[`label_suffix`](fields.html#django.forms.Field.label_suffix "django.forms.Field.label_suffix")。而且將優先于[`Form.label_suffix`](#django.forms.Form.label_suffix "django.forms.Form.label_suffix")。在運行時刻,后綴可以使用[`label_tag()`](#django.forms.BoundField.label_tag "django.forms.BoundField.label_tag") 的`label_suffix` 參數覆蓋。
### 字段的順序
在`as_p()`、`as_ul()` 和`as_table()` 中,字段以表單類中定義的順序顯示。例如,在`ContactForm` 示例中,字段定義的順序為`subject`, `message`, `sender`, `cc_myself`。若要重新排序HTML 中的輸出,只需改變字段在類中列出的順序。
### 錯誤如何顯示
如果你渲染一個綁定的`表單`對象,渲染時將自動運行表單的驗證,HTML 輸出將在出錯字段的附近以`<ul class="errorlist">` 形式包含驗證的錯誤。錯誤信息的位置與你使用的輸出方法有關:
```
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid email address',
... 'cc_myself': True}
>>> f = ContactForm(data, auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" /></td></tr>
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" value="Hi there" /></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" /></li>
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="email" name="sender" value="invalid email address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
```
### 自定義錯誤清單的格式
默認情況下,表單使用`django.forms.utils.ErrorList` 來格式化驗證時的錯誤。如果你希望使用另外一種類來顯示錯誤,可以在構造時傳遞(在Python 2 中將 `__str__` 替換為`__unicode__`):
```
>>> from django.forms.utils import ErrorList
>>> class DivErrorList(ErrorList):
... def __str__(self): # __unicode__ on Python 2
... return self.as_divs()
... def as_divs(self):
... if not self: return ''
... return '<div class="errorlist">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self])
>>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)
>>> f.as_p()
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
<p>Sender: <input type="email" name="sender" value="invalid email address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
```
Changed in Django 1.7:
`django.forms.util` 重命名為`django.forms.utils`。
### 更細粒度的輸出
`as_p()`、`as_ul()` 和`as_table()` 方法是為懶惰的程序員準備的簡單快捷方法 —— 它們不是顯示表單的唯一方式。
_class _`BoundField`
用于顯示HTML 表單或者訪問[`表單`](#django.forms.Form "django.forms.Form")實例的一個屬性。
其`__str__()`(Python 2 上為`__unicode__`)方法顯示該字段的HTML。
以字段的名稱為鍵,用字典查詢語法查詢表單,可以獲取一個 `BoundField`:
```
>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" />
```
迭代表單可以獲取所有的`BoundField`:
```
>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="email" name="sender" id="id_sender" />
<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" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />
```
若要獲取字段的錯誤列表,可以訪問字段的`errors` 屬性。
`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" />
>>> 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)
''
```
`BoundField.``label_tag`(_contents=None_, _attrs=None_, _label_suffix=None_)
可以調用`label_tag` 方法單獨渲染表單字段的label 標簽:
```
>>> f = ContactForm(data)
>>> print(f['message'].label_tag())
<label for="id_message">Message:</label>
```
如果你提供一個可選的`contents` 參數,它將替換自動生成的label 標簽。另外一個可選的`attrs` 參數可以包含`<label>` 標簽額外的屬性。
生成的HTML 包含表單的[`label_suffix`](#django.forms.Form.label_suffix "django.forms.Form.label_suffix")(默認為一個冒號),或者當前字段的[`label_suffix`](fields.html#django.forms.Field.label_suffix "django.forms.Field.label_suffix")。可選的`label_suffix` 參數允許你覆蓋之前設置的后綴。例如,你可以使用一個空字符串來隱藏已選擇字段的label。如果在模板中需要這樣做,你可以編寫一個自定義的過濾器來允許傳遞參數給`label_tag`。
Changed in Django 1.8:
如果可用,label 將包含[`required_css_class`](#django.forms.Form.required_css_class "django.forms.Form.required_css_class")。
`BoundField.``css_classes`()
當你使用Django 的快捷的渲染方法時,習慣使用CSS 類型來表示必填的表單字段和有錯誤的字段。如果你是手工渲染一個表單,你可以使用`css_classes` 方法訪問這些CSS 類型:
```
>>> f = ContactForm(data)
>>> f['message'].css_classes()
'required'
```
除了錯誤和必填的類型之外,如果你還想提供額外的類型,你可以用參數傳遞它們:
```
>>> f = ContactForm(data)
>>> f['message'].css_classes('foo bar')
'foo bar required'
```
`BoundField.``value`()
這個方法用于渲染字段的原始值,與用`Widget` 渲染的值相同:
```
>>> initial = {'subject': 'welcome'}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data, initial=initial)
>>> print(unbound_form['subject'].value())
welcome
>>> print(bound_form['subject'].value())
hi
```
`BoundField.``id_for_label`
使用這個屬性渲染字段的ID。例如,如果你在模板中手工構造一個`<label>`(盡管 [`label_tag()`](#django.forms.BoundField.label_tag "django.forms.BoundField.label_tag") 將為你這么做):
```
<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
```
默認情況下,它是在字段名稱的前面加上`id_` (上面的例子中將是“`id_my_field`”)。你可以通過設置字段Widget 的[`attrs`](widgets.html#django.forms.Widget.attrs "django.forms.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" />
```
## 綁定上傳的文件到表單
處理帶有`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', <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()
```
### 測試multipart 表單
`Form.``is_multipart`()
如果你正在編寫可重用的視圖或模板,你可能事先不知道你的表單是否是一個multipart 表單。`is_multipart()` 方法告訴你表單提交時是否要求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>
```
## 子類化表單
如果你有多個`表單`類共享相同的字段,你可以使用子類化來減少冗余。
當你子類化一個自定義的`表單`類時,生成的子類將包含父類中的所有字段,以及在子類中定義的字段。
在下面的例子中,`ContactFormWithPriority` 包含`ContactForm` 中的所有字段,以及另外一個字段`priority`。排在前面的是`ContactForm` 中的字段:
```
>>> class ContactFormWithPriority(ContactForm):
... priority = forms.CharField()
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="email" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
<li>Priority: <input type="text" name="priority" /></li>
```
可以子類化多個表單,將表單作為“mix-ins”。在下面的例子中,`BeatleForm` 子類化`PersonForm` 和 `InstrumentForm` ,所以它的字段列表包含兩個父類的所有字段:
```
>>> from django.forms import Form
>>> class PersonForm(Form):
... first_name = CharField()
... last_name = CharField()
>>> class InstrumentForm(Form):
... instrument = CharField()
>>> class BeatleForm(PersonForm, InstrumentForm):
... haircut_type = CharField()
>>> b = BeatleForm(auto_id=False)
>>> print(b.as_ul())
<li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li>
<li>Instrument: <input type="text" name="instrument" /></li>
<li>Haircut type: <input type="text" name="haircut_type" /></li>
```
New in Django 1.7\.
* 在子類中,可以通過設置名字為`None` 來刪除從父類中繼承的`字段`。例如:
```
>>> from django import forms
>>> class ParentForm(forms.Form):
... name = forms.CharField()
... age = forms.IntegerField()
>>> class ChildForm(ParentForm):
... name = None
>>> ChildForm().fields.keys()
... ['age']
```
## 表單前綴
`Form.``prefix`
你可以將幾個Django 表單放在一個`<form>` 標簽中。為了給每個`表單`一個自己的命名空間,可以使用`prefix` 關鍵字參數:
```
>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother.as_ul())
<li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" /></li>
<li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" /></li>
>>> print(father.as_ul())
<li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li>
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li>
```
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Form API](https://docs.djangoproject.com/en/1.8/ref/forms/api/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架