# Django 6.0.7 documentation

# 表单 API

关于此文档

本文档介绍了 Django 的表单 API 的具体细节。你应该先阅读 使用表单的介绍。

## 绑定和非绑定表单

一个 `Form` 实例要么是 绑定 到一组数据，要么是 未绑定。

- 如果是 绑定 了一组数据，它就能够验证这些数据，并将表单渲染成 HTML，并在 HTML 中显示数据。

- 如果是 未绑定，它就不能进行验证（因为没有数据可验证！），但它仍然可以将空白表单渲染为 HTML。

class Form

要创建一个未绑定的 `Form` 实例，实例化这个类：

    >>> f = ContactForm()

要将数据绑定到表单，将数据作为字典传递给 `Form` 类构造函数的第一个参数：

    >>> data = {
    ...     "subject": "hello",
    ...     "message": "Hi there",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": 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",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": True,
    ... }
    >>> f = ContactForm(data)
    >>> f.is_valid()
    True

Let's try with some invalid data. In this case, `subject` is blank (an error, because all fields are required by default) and `contact_email` is not a valid email address:

    >>> data = {
    ...     "subject": "",
    ...     "message": "Hi there",
    ...     "contact_email": "invalid email address",
    ...     "urgent": True,
    ... }
    >>> f = ContactForm(data)
    >>> f.is_valid()
    False

Form.errors

访问 `errors` 属性以获取错误消息的字典：

    >>> f.errors
    {'subject': ['This field is required.'],
     'contact_email': ['Enter a valid email address.']}

在这个字典中，键是字段名，值是代表错误信息的字符串列表。错误信息存储在列表中，因为一个字段可以有多个错误信息。

你可以访问 `errors`，而不必先调用 `is_valid()`。无论是调用 `is_valid()` 还是访问 `errors`，表单的数据都会首先被验证。

验证例程只会被调用一次，无论你访问 `errors` 或调用 `is_valid()` 多少次。这意味着，如果验证有副作用，这些副作用将只被触发一次。

Form.errors.as_data()

返回一个 `dict`，将字段映射到它们的原始 `ValidationError` 实例。

    >>> f.errors.as_data()
    {'subject': [ValidationError(['This field is required.'])],
     'contact_email': [ValidationError(['Enter a valid email address.'])]}

Use this method anytime you need to identify an error by its `code`. This enables things like rewriting the error's message or writing custom logic in a view when a given error is present. It can also be used to serialize the errors in a custom format (e.g. XML); for instance, `as_json()` relies on `as_data()`.

需要使用 `as_data()` 方法是由于向后兼容性。以前，`ValidationError` 实例一旦被添加到 `Form.errors` 字典中，其 渲染的 错误信息就会丢失。理想情况下，`Form.errors` 会存储 `ValidationError` 实例，并且带有 `as_` 前缀的方法可以渲染它们，但为了不破坏那些期望在 `Form.errors` 中渲染错误信息的代码，必须反过来做。

Form.errors.as_json(escape_html=False)

Returns a string with the errors serialized as JSON.

    >>> f.errors.as_json()
    '{"subject": [{"message": "This field is required.", "code": "required"}],
     "contact_email": [{"message": "Enter a valid email address.", "code": "invalid"}]}'

默认情况下，`as_json()` 不会转义其输出。如果你使用它来处理类似 AJAX 请求的表单视图，客户端解释响应并将错误插入到页面中，你会希望确保在客户端转义结果，以避免跨站点脚本攻击的可能性。你可以在 JavaScript 中使用 `element.textContent = errorText` 或者使用 jQuery 的 `$(el).text(errorText)` （而不是它的 `.html()` 函数）来实现。

如果出于某些原因你不想使用客户端转义，你也可以设置 `escape_html=True`，错误信息将被转义，这样你就可以直接在 HTML 中使用它们。

Form.errors.get_json_data(escape_html=False)

Returns the errors as a dictionary suitable for serializing to JSON. `Form.errors.as_json()` returns serialized JSON, while this returns the error data before it's serialized.

The `escape_html` parameter behaves as described in `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`，并且在实例化表单时包括了 `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`。可调用的值将被执行。

It is recommended to use `BoundField.initial` over `get_initial_for_field()` because `BoundField.initial` has a simpler interface. Also, unlike `get_initial_for_field()`, `BoundField.initial` caches its values. This is useful especially when dealing with callables whose return values can change (e.g. `datetime.now` or `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()

当你需要检查表单数据是否与初始数据发生变化时，请使用 `has_changed()` 方法。

    >>> data = {
    ...     "subject": "hello",
    ...     "message": "Hi there",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": True,
    ... }
    >>> f = ContactForm(data, initial=data)
    >>> f.has_changed()
    False

当表单提交后，我们会重新构建表单，并提供原始数据，以便进行对比。

    >>> f = ContactForm(request.POST, initial=data)
    >>> f.has_changed()
    True

如果来自 `request.POST` 的数据与 `initial` 中提供的数据不同，那么 `has_changed()` 将为 `True`，否则为 `False`。结果是通过调用 `Field.has_changed()` 对表单中的每个字段进行计算。

Form.changed_data

`changed_data` 属性返回表单绑定数据（通常是 `request.POST`）中与 `initial` 中提供的数据不同的字段名列表。如果没有不同的数据，则返回一个空列表。

    >>> f = ContactForm(request.POST, initial=data)
    >>> f.changed_data
    ['subject', 'message']

## 访问表单中的字段

Form.fields

您可以通过表单实例的 `fields` 属性访问 `Form` 的字段：

    >>> 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",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": True,
    ... }
    >>> f = ContactForm(data)
    >>> f.is_valid()
    True
    >>> f.cleaned_data
    {'subject': 'hello', 'message': 'Hi there', 'contact_email': 'foo@example.com', 'urgent': True}

请注意，任何基于文本的字段——如 `CharField` 或 `EmailField`——总是将输入清理成一个字符串。我们将在本文档后面介绍编码的含义。

如果你的数据验证不通过，`cleaned_data` 字典仅包含有效的字段：

    >>> data = {
    ...     "subject": "",
    ...     "message": "Hi there",
    ...     "contact_email": "invalid email address",
    ...     "urgent": True,
    ... }
    >>> f = ContactForm(data)
    >>> f.is_valid()
    False
    >>> f.cleaned_data
    {'message': 'Hi there', 'urgent': True}

`cleaned_data` 将始终仅包含在 `Form` 中定义的字段的键，即使在定义 `Form` 时传递了额外的数据。在这个例子中，我们在 `ContactForm` 构造函数中传递了许多额外字段，但 `cleaned_data` 仅包含表单的字段：

    >>> data = {
    ...     "subject": "hello",
    ...     "message": "Hi there",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": 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.
    {'subject': 'hello', 'message': 'Hi there', 'contact_email': 'foo@example.com', 'urgent': True}

When the `Form` is valid, `cleaned_data` will include a key and value for all its fields, even if the data didn't include a value for some optional fields. In this example, the data dictionary doesn't include a value for the `nickname` field, but `cleaned_data` includes it, with an empty value:

    >>> from django import forms
    >>> class OptionalPersonForm(forms.Form):
    ...     first_name = forms.CharField()
    ...     last_name = forms.CharField()
    ...     nickname = forms.CharField(required=False)
    ...
    >>> data = {"first_name": "John", "last_name": "Lennon"}
    >>> f = OptionalPersonForm(data)
    >>> f.is_valid()
    True
    >>> f.cleaned_data
    {'first_name': 'John', 'last_name': 'Lennon', 'nickname': ''}

In this above example, the `cleaned_data` value for `nickname` is set to an empty string, because `nickname` is `CharField`, and `CharField`s treat empty values as an empty string. Each field type knows what its "blank" value is -- e.g., for `DateField`, it's `None` instead of the empty string. For full details on each field's behavior in this case, see the "Empty value" note for each field in the 内置 Field 类 section below.

你可以编写代码来对特定的表单字段（基于其名称）或整个表单（考虑各种字段的组合）进行验证。更多关于这方面的信息请参见 表单和字段验证。

## 将表单输出为 HTML

一个 `Form` 对象的第二个任务是将自己呈现为 HTML。要做到这一点，只需将它打印出来：

    >>> 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><textarea name="message" cols="40" rows="10" required id="id_message"></textarea></div>
    <div><label for="id_contact_email">Contact email:</label><input type="email" name="contact_email" maxlength="320" required id="id_contact_email"></div>
    <div><label for="id_urgent">Urgent:</label><input type="checkbox" name="urgent" id="id_urgent"></div>

如果表单与数据绑定，HTML 输出将适当地包含该数据。例如，如果一个字段由 `<input type="text">` 表示，数据将位于 `value` 属性中。如果一个字段由 `<input type="checkbox">` 表示，那么 HTML 中将包含 `checked` （如果适用）：

    >>> data = {
    ...     "subject": "hello",
    ...     "message": "Hi there",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": 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><textarea name="message" cols="40" rows="10" required id="id_message">Hi there</textarea></div>
    <div><label for="id_contact_email">Contact email:</label><input type="email" name="contact_email" value="foo@example.com" maxlength="320" required id="id_contact_email"></div>
    <div><label for="id_urgent">Urgent:</label><input type="checkbox" name="urgent" id="id_urgent" 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` 类中的属性名中提取。

- The text label for each field -- e.g. `'Subject:'`, `'Message:'` and `'Contact email:'` is generated from the field name by converting all underscores to spaces and upper-casing the first letter. Again, note these are merely sensible defaults; you can also specify labels manually.

- 每个文本标签都被一个 HTML `<label>` 标签包围，该标签通过其 `id` 指向相应的表格字段。而它的 `id` 则是通过在字段名前加上 `'id_'` 生成的。`id` 属性和 `<label>` 标签默认包含在输出中，以遵循最佳实践，但你可以改变这种行为。

- 输出使用 HTML5 语法，目标是 `<!DOCTYPE html>`。例如，它使用布尔属性，如 `checked` 而不是 XHTML 风格的 `checked='checked'`。

虽然 `<div>` 输出是在打印表单时的默认输出样式，但你可以通过使用自己的表单模板来自定义输出，这可以在整个站点范围、每个表单或每个实例中进行设置。请参阅 可复用的表单模板。

### 默认渲染

当你打印一个表单时，默认的渲染使用以下方法和属性。

#### `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()` 时使用。可以通过覆盖此属性来更改每个表单的模板，也可以通过覆盖默认模板来更一般地更改，参见 覆盖内置表单模板。

### 输出样式

更改表单输出样式的推荐方法是设置自定义表单模板，可以是整个站点范围、每个表单或每个实例。有关示例，请参阅 可复用的表单模板。

以下的辅助函数是为了向后兼容而提供的，它们是对 `Form.render()` 的代理，传递了特定的 `template_name` 值。

Note

在框架提供的模板和输出样式中，推荐使用默认的 `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()

... gives HTML like:

    <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>
    <textarea name="message" cols="40" rows="10" required id="id_message"></textarea>
    </div>
    <div>
    <label for="id_contact_email">Contact email:</label>
    <input type="email" name="contact_email" required id="id_contact_email">
    </div>
    <div>
    <label for="id_urgent">Urgent:</label>
    <input type="checkbox" name="urgent" id="id_urgent">
    </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()

... gives HTML like:

    <p><label for="id_subject">Subject:</label> <input type="text" name="subject" maxlength="100" required id="id_subject"></p>
    <p><label for="id_message">Message:</label> <textarea name="message" cols="40" rows="10" required id="id_message"></textarea></p>
    <p><label for="id_contact_email">Contact email:</label> <input type="email" name="contact_email" maxlength="320" required id="id_contact_email"></p>
    <p><label for="id_urgent">Urgent:</label> <input type="checkbox" name="urgent" id="id_urgent"></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()

... gives HTML like:

    <li><label for="id_subject">Subject:</label> <input type="text" name="subject" maxlength="100" required id="id_subject"></li>
    <li><label for="id_message">Message:</label> <textarea name="message" cols="40" rows="10" required id="id_message"></textarea></li>
    <li><label for="id_contact_email">Contact email:</label> <input type="email" name="contact_email" maxlength="320" required id="id_contact_email"></li>
    <li><label for="id_urgent">Urgent:</label> <input type="checkbox" name="urgent" id="id_urgent"></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()

... gives HTML like:

    <tr><th><label for="id_subject">Subject:</label></th><td><input type="text" name="subject" maxlength="100" required id="id_subject"></td></tr>
    <tr><th><label for="id_message">Message:</label></th><td><textarea name="message" cols="40" rows="10" required id="id_message"></textarea></td></tr>
    <tr><th><label for="id_contact_email">Contact email:</label></th><td><input type="email" name="contact_email" maxlength="320" required id="id_contact_email"></td></tr>
    <tr><th><label for="id_urgent">Urgent:</label></th><td><input type="checkbox" name="urgent" id="id_urgent"></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_contact_email" class="required">Contact email:</label> ...
    <div><label for="id_urgent">Urgent:</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>

You may further modify the rendering of form rows by using a custom BoundField.

### 设置表单元素的 HTML `id` 属性和 `<label>` 标签。

Form.auto_id

默认情况下，表单渲染方法包括：

- 表单元素的 HTML `id` 属性。

- 标签周围对应的 `<label>` 标签。HTML `<label>` 标签指定了哪个标签文本与哪个表单元素相关联。这个小小的改进使表单更加可用，也更容易被辅助设备访问。使用 `<label>` 标签总是一个好主意。

The `id` attribute values are generated by prepending `id_` to the form field names. This behavior is configurable, though, if you want to change the `id` convention or remove HTML `id` attributes and `<label>` tags entirely.

使用 `Form` 构造函数的 `auto_id` 参数来控制 `id` 和标签行为。这个参数必须是 `True`、`False` 或一个字符串。

如果 `auto_id` 是 `False`，那么表单输出将不包含 `<label>` 标签和 `id` 属性：

    >>> f = ContactForm(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>Contact email:<input type="email" name="contact_email" required></div>
    <div>Urgent:<input type="checkbox" name="urgent"></div>

如果 `auto_id` 设置为 `True`，那么表单输出将包含 `<label>` 标签，并且将使用字段名作为每个表单字段的 `id`：

    >>> f = ContactForm(auto_id=True)
    >>> print(f)
    <div><label for="subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="subject"></div>
    <div><label for="message">Message:</label><textarea name="message" cols="40" rows="10" required id="message"></textarea></div>
    <div><label for="contact_email">Contact email:</label><input type="email" name="contact_email" required id="contact_email"></div>
    <div><label for="urgent">Urgent:</label><input type="checkbox" name="urgent" id="urgent"></div>

如果 `auto_id` 设置为包含格式字符 `'%s'` 的字符串，那么表单输出将包含 `<label>` 标签，并且将根据格式字符串生成 `id` 属性。例如，对于格式字符串 `'field_%s'`，名为 `subject` 的字段将获得 `id` 值 `'field_subject'`。继续我们的例子：

    >>> f = ContactForm(auto_id="id_for_%s")
    >>> print(f)
    <div><label for="id_for_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
    <div><label for="id_for_message">Message:</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
    <div><label for="id_for_contact_email">Contact email:</label><input type="email" name="contact_email" required id="id_for_contact_email"></div>
    <div><label for="id_for_urgent">Urgent:</label><input type="checkbox" name="urgent" id="id_for_urgent"></div>

如果 `auto_id` 被设置为任何其他的真值——比如一个不包含 `%s` 的字符串——那么该库就会像 `auto_id` 是 `True` 一样。

默认情况下，`auto_id` 被设置为字符串 `'id_%s'`。

Form.label_suffix

一个可翻译的字符串（英文默认为冒号（`:`）），将在渲染表格时附加在任何标签名称之后。

可以使用 `label_suffix` 参数自定义该字符，或完全省略它：

    >>> f = ContactForm(auto_id="id_for_%s", label_suffix="")
    >>> print(f)
    <div><label for="id_for_subject">Subject</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
    <div><label for="id_for_message">Message</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
    <div><label for="id_for_contact_email">Contact email</label><input type="email" name="contact_email" required id="id_for_contact_email"></div>
    <div><label for="id_for_urgent">Urgent</label><input type="checkbox" name="urgent" id="id_for_urgent"></div>
    >>> f = ContactForm(auto_id="id_for_%s", label_suffix=" ->")
    >>> print(f)
    <div><label for="id_for_subject">Subject -&gt;</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
    <div><label for="id_for_message">Message -&gt;</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
    <div><label for="id_for_contact_email">Contact email -&gt;</label><input type="email" name="contact_email" required id="id_for_contact_email"></div>
    <div><label for="id_for_urgent">Urgent -&gt;</label><input type="checkbox" name="urgent" id="id_for_urgent"></div>

请注意，只有当标签的最后一个字符不是标点符号时，才会加上标签后缀（在英文中，这些字符是 `.`、`!`、`?` 或 `:`）。

字段还可以定义自己的 `label_suffix`。这将优先于 `Form.label_suffix`。后缀也可以在运行时使用 `label_suffix` 参数来覆盖 `label_tag()`/ `legend_tag()`。

Form.use_required_attribute

当设置为 `True` （默认）时，必填表单字段将有 `required` HTML 属性。

表单集 实例化表单时使用 `use_required_attribute=False` 以避免从表单集中添加和删除表单时浏览器验证错误。

### 设置表单组件的渲染方式

Form.default_renderer

Specifies the renderer to use for the form. Defaults to `None` which means to use the default renderer specified by the `FORM_RENDERER` setting.

你可以在声明你的表单时将其设置为一个类属性，或者使用 `Form.__init__()` 的 `renderer` 参数。例如：

    from django import forms

    class MyForm(forms.Form):
        default_renderer = MyRenderer()

或者：

    form = MyForm(renderer=MyRenderer())

### 字段顺序的注意事项

In the `as_p()`, `as_ul()` and `as_table()` shortcuts, the fields are displayed in the order in which you define them in your form class. For example, in the `ContactForm` example, the fields are defined in the order `subject`, `message`, `contact_email`, `urgent`. To reorder the HTML output, change the order in which those fields are listed in the class.

还有其他几种方式可以自定义顺序：

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`。

### 如何显示错误

If you render a bound `Form` object, the act of rendering will automatically run the form's validation if it hasn't already happened, and the HTML output will include the validation errors as a `<ul class="errorlist">`.

The following:

    >>> data = {
    ...     "subject": "",
    ...     "message": "Hi there",
    ...     "contact_email": "invalid email address",
    ...     "urgent": True,
    ... }
    >>> ContactForm(data).as_div()

... gives HTML like:

    <div>
      <label for="id_subject">Subject:</label>
      <ul class="errorlist" id="id_subject_error"><li>This field is required.</li></ul>
      <input type="text" name="subject" maxlength="100" required aria-invalid="true" aria-describedby="id_subject_error" id="id_subject">
    </div>
    <div>
      <label for="id_message">Message:</label>
      <textarea name="message" cols="40" rows="10" required id="id_message">Hi there</textarea>
    </div>
    <div>
      <label for="id_contact_email">Contact email:</label>
      <ul class="errorlist" id="id_contact_email_error"><li>Enter a valid email address.</li></ul>
      <input type="email" name="contact_email" value="invalid email address" maxlength="320" required aria-invalid="true" aria-describedby="id_contact_email_error" id="id_contact_email">
    </div>
    <div>
        <label for="id_urgent">Urgent:</label>
        <input type="checkbox" name="urgent" id="id_urgent" checked>
    </div>

Django's default form templates will associate validation errors with their input by using the `aria-describedby` HTML attribute when the field has an `auto_id` and a custom `aria-describedby` is not provided. If a custom `aria-describedby` is set when defining the widget this will override the default value.

If the widget is rendered in a `<fieldset>` then `aria-describedby` is added to this element, otherwise it is added to the widget's HTML element (e.g. `<input>`).

### 自定义错误列表格式

class ErrorList(initlist=None, error_class=None, renderer=None, field_id=None)

  默认情况下，表单使用 `django.forms.utils.ErrorList` 来格式化验证错误。`ErrorList` 是一个类似列表的对象，其中 `initlist` 是错误列表。此外，这个类有以下属性和方法。

  error_class

    渲染错误列表时要使用的 CSS 类。任何提供的类将被添加到默认的 `errorlist` 类中。

  renderer

    Specifies the renderer to use for `ErrorList`. Defaults to `None` which means to use the default renderer specified by the `FORM_RENDERER` setting.

  field_id

    An `id` for the field for which the errors relate. This allows an HTML `id` attribute to be added in the error template and is useful to associate the errors with the field. The default template uses the format `id="{{ field_id }}_error"` and a value is provided by `Form.add_error()` using the field's `auto_id`.

  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>`。

  get_context()

    返回模板中渲染错误的上下文。

    可用的上下文：

    - `errors`： 一个错误列表。

    - `error_class`：一个 CSS 类的字符串。

  render(template_name=None, context=None, renderer=None)

    渲染方法被 `__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()` 方法都是快捷方式——它们并不是显示表单对象的唯一方式。

class BoundField

  用于显示 `Form` 实例的单个字段的 HTML 或访问属性。

  该对象的 `__str__()` 方法显示该字段的 HTML。

  You can use `Form.bound_field_class` and `Field.bound_field_class` to specify a different `BoundField` class per form or per field, respectively.

  See 自定义 BoundField for examples of overriding a `BoundField`.

要检索单个 `BoundField`，可以在表单上使用字段的名称作为键进行字典查找：

    >>> form = ContactForm()
    >>> print(form["subject"])
    <input type="text" name="subject" maxlength="100" required id="id_subject">

要检索所有的 `BoundField` 对象，可以迭代表单：

    >>> form = ContactForm()
    >>> for boundfield in form:
    ...     print(boundfield)
    ...
    <input type="text" name="subject" maxlength="100" required id="id_subject">
    <textarea name="message" cols="40" rows="10" required id="id_message"></textarea>
    <input type="email" name="contact_email" maxlength="320" required id="id_contact_email">
    <input type="checkbox" name="urgent" id="id_urgent">

字段特定的输出遵循表单对象的 `auto_id` 设置：

    >>> f = ContactForm(auto_id=False)
    >>> print(f["message"])
    <textarea name="message" cols="40" rows="10" required></textarea>
    >>> f = ContactForm(auto_id="id_%s")
    >>> print(f["message"])
    <textarea name="message" cols="40" rows="10" required id="id_message"></textarea>

### `BoundField` 的属性

BoundField.aria_describedby

  Returns an `aria-describedby` reference to associate a field with its help text and errors. Returns `None` if `aria-describedby` is set in `Widget.attrs` to preserve the user defined attribute when rendering the form.

BoundField.auto_id

  `BoundField` 的 HTML ID 属性。如果 `Form.auto_id` 是 `False`，则返回一个空字符串。

BoundField.data

  该属性返回由 widget 的 `value_from_datadict()` 方法提取的这个 `BoundField` 的数据，如果没有提供数据则返回 `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": "", "contact_email": "", "urgent": ""}
      >>> 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)
      ''

  当渲染带有错误的字段时，将在字段的小部件上设置 `aria-invalid="true"`，以向屏幕阅读器用户指示存在错误。

BoundField.field

  这个 `Field` 封装的表单类中的表单 `BoundField` 实例。

BoundField.form

  这个 `Form` 实例与这个 `BoundField` 绑定。

BoundField.help_text

  字段的 `help_text`。

BoundField.html_name

  部件的 HTML `name` 属性中使用的名称。它考虑到了 `prefix` 的形式。

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`"）。你可以通过设置 `attrs` 对字段的部件进行修改。例如，声明一个字段是这样的：

      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)

  Using `BoundField.initial` is recommended over `get_initial_for_field()`.

BoundField.is_hidden

  如果这个 `BoundField` 的部件被隐藏，返回 `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

  使用 `BoundField.as_field_group()` 渲染的模板的名称。

  如果设置了 `template_name`，则返回该值，否则返回 `field_template_name` 的值的属性。

BoundField.use_fieldset

  返回该 BoundField widget 的 `use_fieldset` 属性的值。

BoundField.widget_type

  返回封装字段 widget 的类名的小写形式，去除任何尾随的 `input` 或 `widget`。这可以在构建依赖于 widget 类型的表单时使用。例如：

      {% for field in form %}
          {% if field.widget_type == 'checkbox' %}
              # render one way
          {% else %}
              # render another way
          {% endif %}
      {% endfor %}

### `BoundField` 方法

BoundField.as_field_group()

  使用默认值使用 `BoundField.render()` 渲染字段，渲染 `BoundField`，包括其标签、帮助文本和错误，使用模板的 `template_name` （如果设置）或者使用 `field_template_name`。

BoundField.as_hidden(attrs=None, **kwargs)

  返回将其表示为 `<input type="hidden">` 的 HTML 字符串。

  `**kwargs` 传递给 `as_widget()`。

  这个方法主要在内部使用。你应该使用部件来代替。

BoundField.as_widget(widget=None, attrs=None, only_initial=False)

  Renders the field by rendering the passed widget, adding any HTML attributes passed as `attrs`. If no widget is specified, then the field's default widget will be used.

  `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.get_context()

  返回用于渲染字段的模板上下文。可用的上下文是 `field`，它是绑定字段的实例。

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` 是由字段的部件 `attrs` 或 `BoundField.auto_id` 产生的。其他的属性可以由 `attrs` 参数提供。

  - `use_tag`: 一个布尔值，如果标签具有 `id`，则为 `True`。如果为 `False`，默认模板将省略 `tag`。

  - `tag`: 可选的字符串，用于自定义标签，默认为 `label`。

  Tip

  在你的模板中 `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>` 标签呈现标签。这在呈现单选按钮和多选框小部件时非常有用，其中 `<legend>` 可能比 `<label>` 更合适。

BoundField.render(template_name=None, context=None, renderer=None)

  渲染方法由 `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`

Form.bound_field_class

Define a custom `BoundField` class to use when rendering the form. This takes precedence over the project-level `BaseRenderer.bound_field_class` (along with a custom `FORM_RENDERER`), but can be overridden by the field-level `Field.bound_field_class`.

If not defined as a class variable, `bound_field_class` can be set via the `bound_field_class` argument in the `Form` or `Field` constructor.

For compatibility reasons, a custom form field can still override `Field.get_bound_field()` to use a custom class, though any of the previous options are preferred.

You may want to use a custom `BoundField` if you need to access some additional information about a form field in a template and using a subclass of `Field` isn't sufficient.

For example, if you have a `GPSCoordinatesField`, and want to be able to access additional information about the coordinates in a template, this could be implemented as follows:

    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):
        bound_field_class = GPSCoordinatesBoundField

现在你可以在模板中使用 `{{form.coordinates.country }}` 访问国家。

You may also want to customize the default form field template rendering. For example, you can override `BoundField.label_tag()` to add a custom class:

    class StyledLabelBoundField(BoundField):
        def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
            attrs = attrs or {}
            attrs["class"] = "wide"
            return super().label_tag(contents, attrs, label_suffix, tag)

    class UserForm(forms.Form):
        bound_field_class = StyledLabelBoundField
        name = CharField()

This would update the default form rendering:

    >>> f = UserForm()
    >>> print(f["name"].label_tag)
    <label for="id_name" class="wide">Name:</label>

To add a CSS class to the wrapping HTML element of all fields, a `BoundField` can be overridden to return a different collection of CSS classes:

    class WrappedBoundField(BoundField):
        def css_classes(self, extra_classes=None):
            parent_css_classes = super().css_classes(extra_classes)
            return f"field-class {parent_css_classes}".strip()

    class UserForm(forms.Form):
        bound_field_class = WrappedBoundField
        name = CharField()

This would update the form rendering as follows:

    >>> f = UserForm()
    >>> print(f)
    <div class="field-class"><label for="id_name">Name:</label><input type="text" name="name" required id="id_name"></div>

Alternatively, to override the `BoundField` class at the project level, `BaseRenderer.bound_field_class` can be defined on a custom `FORM_RENDERER`:

`mysite/renderers.py`

    from django.forms.renderers import DjangoTemplates

    from .forms import CustomBoundField

    class CustomRenderer(DjangoTemplates):
        bound_field_class = CustomBoundField

`settings.py`

    FORM_RENDERER = "mysite.renderers.CustomRenderer"

## 将上传的文件绑定到表单中

处理有 `FileField` 和 `ImageField` 字段的表单比普通表单要复杂一些。

首先，为了上传文件，你需要确保你的 `<form>` 元素正确地将 `enctype` 定义为 `"multipart/form-data"`：

    <form enctype="multipart/form-data" method="post" action="/foo/">

其次，当你使用表单时，你需要绑定文件数据。文件数据与普通表单数据分开处理，所以当你的表单包含一个 `FileField` 和 `ImageField` 时，你需要在绑定表单时指定第二个参数。因此，如果我们扩展我们的 ContactForm 以包括一个名为 `mugshot` 的 `ImageField`，我们需要绑定包含头像图片的文件数据：

    # Bound form with an image field
    >>> from django.core.files.uploadedfile import SimpleUploadedFile
    >>> data = {
    ...     "subject": "hello",
    ...     "message": "Hi there",
    ...     "contact_email": "foo@example.com",
    ...     "urgent": 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` 类子类化时，生成的子类将包括父类的所有字段，然后是你在子类中定义的字段。

In this example, `ContactFormWithDepartment` contains all the fields from `ContactForm`, plus an additional field, `department`. The `ContactForm` fields are ordered first:

    >>> class ContactFormWithDepartment(ContactForm):
    ...     department = forms.CharField()
    ...
    >>> f = ContactFormWithDepartment(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>Contact email:<input type="email" name="contact_email" required></div>
    <div>Urgent:<input type="checkbox" name="urgent"></div>
    <div>Department:<input type="text" name="department" required></div>

可以子类化多个表单，将表单视为混合类。在这个例子中，`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

你可以将多个 Django 表单放在同一个 `<form>` 标签内。为每个 `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"
    ...