# 表单 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格式化输入
- “本地特色”
- 常见的网站应用工具
- 认证
- 概览
- 使用认证系统
- 密码管理
- 日志
- 分页
- 会话
- 数据验证
- 其它核心功能
- 按需内容处理
- 重定向
- 信号
- 系统检查框架