表单 API — Django 文档

来自菜鸟教程
Django/docs/3.2.x/ref/forms/api
跳转至:导航、​搜索

表单 API

关于本文件

本文档涵盖了 Django 表单 API 的详细信息。 您应该先阅读 使用表单 的介绍。


绑定和未绑定表单

Form 实例要么是 bound 到一组数据,要么是 unbound

  • 如果它 绑定 到一组数据,它能够验证该数据并将表单呈现为 HTML,数据显示在 HTML 中。
  • 如果它是 unbound,则无法进行验证(因为没有要验证的数据!),但它仍然可以将空白表单呈现为 HTML。
class Form

要创建未绑定的 Form 实例,请实例化该类:

>>> f = ContactForm()

要将数据绑定到表单,请将数据作为字典作为第一个参数传递给 Form 类构造函数:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)

在此字典中,键是字段名称,对应于 Form 类中的属性。 这些值是您要验证的数据。 这些通常是字符串,但不要求它们是字符串; 您传递的数据类型取决于 Field,我们稍后会看到。

Form.is_bound

如果需要在运行时区分绑定和未绑定的表单实例,请检查表单的 is_bound 属性的值:

>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True

请注意,传递空字典会创建一个带有空数据的 bound 表单:

>>> f = ContactForm({})
>>> f.is_bound
True

如果您有一个绑定的 Form 实例并想以某种方式更改数据,或者如果您想将未绑定的 Form 实例绑定到某些数据,请创建另一个 Form实例。 无法更改 Form 实例中的数据。 一旦创建了 Form 实例,您应该考虑它的数据不可变,无论它是否有数据。


使用表单验证数据

Form.clean()

当您必须为相互依赖的字段添加自定义验证时,在 Form 上实现 clean() 方法。 请参阅 清理和验证相互依赖的字段 例如用法。

Form.is_valid()

Form 对象的主要任务是验证数据。 使用绑定的 Form 实例,调用 is_valid() 方法运行验证并返回指定数据是否有效的布尔值:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True

让我们尝试一些无效的数据。 在这种情况下,subject 为空(错误,因为默认情况下所有字段都是必需的)并且 sender 不是有效的电子邮件地址:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
Form.errors

访问 errors 属性以获取错误消息字典:

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

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

您可以访问 errors 而无需先调用 is_valid()。 表单的数据将在您第一次调用 is_valid() 或访问 errors 时进行验证。

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

Form.errors.as_data()

返回将字段映射到其原始 ValidationError 实例的 dict

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

您可以随时使用此方法通过 code 识别错误。 当出现给定错误时,这可以实现诸如重写错误消息或在视图中编写自定义逻辑之类的事情。 它还可以用于以自定义格式序列化错误(例如 XML); 例如,as_json() 依赖于 as_data()

需要 as_data() 方法是因为向后兼容。 以前,ValidationError 实例在 渲染的 错误消息添加到 Form.errors 字典后立即丢失。 理想情况下,Form.errors 将存储 ValidationError 实例和具有 as_ 前缀的方法可以渲染它们,但必须以相反的方式完成,以免破坏预期的代码在 Form.errors 中呈现错误消息。

Form.errors.as_json(escape_html=False)

返回序列化为 JSON 的错误。

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

默认情况下,as_json() 不会对其输出进行转义。 如果您将它用于诸如 AJAX 请求的表单视图,其中客户端解释响应并将错误插入页面中,您需要确保在客户端转义结果以避免交叉的可能性-站点脚本攻击。 你可以在 JavaScript 中使用 element.textContent = errorText 或 jQuery 的 $(el).text(errorText)(而不是它的 .html() 函数)来做到这一点。

如果由于某种原因您不想使用客户端转义,您还可以设置 escape_html=True 并且错误消息将被转义,以便您可以直接在 HTML 中使用它们。

Form.errors.get_json_data(escape_html=False)

将错误作为适合序列化为 JSON 的字典返回。 Form.errors.as_json() 返回序列化的 JSON,同时返回序列化之前的错误数据。

escape_html 参数的行为如 Form.errors.as_json() 中所述。

Form.add_error(field, error)

此方法允许从 Form.clean() 方法内部或完全从表单外部向特定字段添加错误; 例如从一个角度来看。

field 参数是应添加错误的字段的名称。 如果其值为 None,则该错误将被视为由 Form.non_field_errors() 返回的非字段错误。

error 参数可以是字符串,或者最好是 ValidationError 的实例。 有关定义表单错误时的最佳实践,请参阅 Raising ValidationError

请注意,Form.add_error() 会自动从 cleaned_data 中删除相关字段。

Form.has_error(field, code=None)

此方法返回一个布尔值,指定一个字段是否有一个带有特定错误 code 的错误。 如果 codeNone,如果字段包含任何错误,它将返回 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 字段。

为此,请使用 Forminitial 参数。 这个参数,如果给定,应该是一个将字段名称映射到初始值的字典。 仅包含您为其指定初始值的字段; 没有必要在表单中包含每个字段。 例如:

>>> f = ContactForm(initial={'subject': 'Hi there!'})

这些值仅针对未绑定的表单显示,如果未提供特定值,它们不会用作后备值。

如果 Field 定义了 initial ,则在实例化 Form 时包含 initial,则后者 [ X141X] 将具有优先权。 在这个例子中,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" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required></td></tr>
Form.get_initial_for_field(field, field_name)

返回表单域的初始数据。 如果存在,它从 Form.initial 检索数据,否则尝试 Field.initial。 评估可调用值。

建议使用 BoundField.initial 而不是 get_initial_for_field(),因为 BoundField.initial 的接口更简单。 此外,与 get_initial_for_field() 不同,BoundField.initial 缓存其值。 这在处理返回值可以改变的可调用对象时尤其有用(例如 datetime.nowuuid.uuid4):

>>> import uuid
>>> class UUIDCommentForm(CommentForm):
...     identifier = forms.UUIDField(initial=uuid.uuid4)
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields['identifier'], 'identifier')
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields['identifier'], 'identifier')
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f['identifier'].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f['identifier'].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')

检查哪些表单数据已更改

Form.has_changed()

当您需要检查表单数据是否已从初始数据更改时,请使用 Form 上的 has_changed() 方法。

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

当表单提交时,我们对其进行重构并提供原始数据,以便进行比较:

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

如果来自 request.POST 的数据与 initialFalse 中提供的数据不同,则 has_changed() 将是 True。 通过为表单中的每个字段调用 Field.has_changed() 来计算结果。

Form.changed_data

changed_data 属性返回表单绑定数据(通常为 request.POST)中的值与 initial 中提供的值不同的字段名称列表。 如果没有数据不同,它返回一个空列表。

>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
...     print("The following fields changed: %s" % ", ".join(f.changed_data))
>>> f.changed_data
['subject', 'message']

从表单访问字段

Form.fields

您可以从 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 实例的字段以更改其在表单中的显示方式:

>>> f.as_table().split('\n')[0]
'<tr><th>Name:</th><td><input name="name" type="text" value="instance" required></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" required></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" required></td></tr>'

访问“干净”的数据

Form.cleaned_data

Form 类中的每个字段不仅负责验证数据,还负责“清理”数据——将其规范化为一致的格式。 这是一个很好的功能,因为它允许以多种方式输入特定字段的数据,始终产生一致的输出。

例如, DateField 将输入规范化为 Python datetime.date 对象。 无论您以 '1994-07-15'datetime.date 对象或许多其他格式向其传递字符串,DateField 始终会将其规范化为 [ X162X] 对象,只要它有效。

一旦您使用一组数据创建了 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'}

请注意,任何基于文本的字段 - 例如 CharFieldEmailField - 总是将输入清除为字符串。 我们将在本文档后面介绍编码含义。

如果您的数据 验证,则 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 将始终 only 包含 Form 中定义的字段的键,即使您在定义 Form 时传递额外数据。 在这个例子中,我们将一堆额外的字段传递给 ContactForm 构造函数,但 cleaned_data 只包含表单的字段:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True,
...         'extra_field_1': 'foo',
...         'extra_field_2': 'bar',
...         'extra_field_3': 'baz'}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

Form 有效时,cleaned_data 将包括 all 其字段的键和值,即使数据不包括某些可选字段的值。 在此示例中,数据字典不包含 nick_name 字段的值,但 cleaned_data 包含它,值为空:

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

在上面的示例中,nick_namecleaned_data 值设置为空字符串,因为 nick_nameCharField,而 CharFields将空值视为空字符串。 每个字段类型都知道它的“空白”值是什么——例如,对于 DateField,它是 None 而不是空字符串。 有关这种情况下每个字段行为的完整详细信息,请参阅下面“内置 Field 类”部分中每个字段的“空值”注释。

您可以编写代码来对特定表单字段(基于其名称)或整个表单(考虑各种字段的组合)执行验证。 关于此的更多信息在 表单和字段验证 中。


将表单输出为 HTML

Form 对象的第二个任务是将自身呈现为 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" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>

如果表单绑定到数据,HTML 输出将适当地包含该数据。 例如,如果字段由 <input type="text"> 表示,则数据将位于 value 属性中。 如果字段由 <input type="checkbox"> 表示,则该 HTML 将在适当时包含 checked

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></td></tr>

此默认输出是一个两列 HTML 表,每个字段都有一个 <tr>。 请注意以下几点:

  • 为了灵活性,输出不 not 包括 <table></table> 标签,也不包括 <form></form> 标签或<input type="submit"> 标签。 这样做是你的工作。
  • 每个字段类型都有一个默认的 HTML 表示。 CharField<input type="text">表示,EmailField<input type="email">表示。 BooleanField(null=False)<input type="checkbox">表示。 请注意,这些只是合理的默认值; 您可以使用小部件指定给给定字段使用的 HTML,我们将在稍后解释。
  • 每个标签的 HTML name 直接取自 ContactForm 类中的属性名称。
  • 每个字段的文本标签——例如 'Subject:''Message:''Cc myself:' 是通过将所有下划线转换为空格并将首字母大写从字段名称生成的。 再次注意,这些只是合理的默认值; 您也可以手动指定标签。
  • 每个文本标签都包含在 HTML <label> 标签中,该标签通过其 id 指向适当的表单字段。 反过来,它的 id 是通过在字段名称前加上 'id_' 来生成的。 id 属性和 <label> 标签默认包含在输出中,以遵循最佳实践,但您可以更改该行为。
  • 输出使用 HTML5 语法,针对 <!DOCTYPE html>。 例如,它使用 checked 等布尔属性,而不是 checked='checked' 的 XHTML 样式。

尽管 <table> 输出是 print 表单时的默认输出样式,但其他输出样式也可用。 每种样式都可用作表单对象上的方法,并且每种呈现方法都返回一个字符串。

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" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>

as_ul()

Form.as_ul()

as_ul() 将表单呈现为一系列 <li> 标签,每个 <li> 包含一个字段。 它确实 not 包括 <ul></ul>,以便您可以在 <ul> 上指定任何 HTML 属性以获得灵活性:

>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>

as_table()

Form.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" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>

样式需要或错误的形式行

Form.error_css_class
Form.required_css_class

为需要或有错误的表单行和字段设置样式是很常见的。 例如,您可能希望以粗体显示所需的表单行并以红色突出显示错误。

Form 类有几个钩子可以用来将 class 属性添加到需要的行或有错误的行:设置 Form.error_css_class 和/或 [ X172X]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.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>

配置表单元素的 HTML id 属性和 <label> 标签

Form.auto_id

默认情况下,表单渲染方法包括:

  • 表单元素上的 HTML id 属性。
  • 标签周围相应的 <label> 标签。 HTML <label> 标签指定哪个标签文本与哪个表单元素相关联。 这个小小的改进使表单更有用,更容易被辅助设备访问。 使用 <label> 标签总是一个好主意。

id 属性值是通过在表单字段名称前加上 id_ 来生成的。 但是,如果您想更改 id 约定或完全删除 HTML id 属性和 <label> 标签,则此行为是可配置的。

使用 Form 构造函数的 auto_id 参数来控制 id 和标签行为。 此参数必须是 TrueFalse 或字符串。

如果 auto_idFalse,那么表单输出将不包含 <label> 标签和 id 属性:

>>> f = ContactForm(auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" required></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" required></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" required></li>
<li>Message: <input type="text" name="message" required></li>
<li>Sender: <input type="email" name="sender" required></li>
<li>Cc myself: <input type="checkbox" name="cc_myself"></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <input type="text" name="message" required></p>
<p>Sender: <input type="email" name="sender" required></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" required></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" required></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" required></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" required></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" required></li>
<li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required></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" required></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" required></p>
<p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself"></p>

如果 auto_id 设置为包含格式字符 '%s' 的字符串,则表单输出将包含 <label> 标签,并会根据格式字符串。 例如,对于格式字符串 '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" required></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" required></td></tr>
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="email" name="sender" id="id_for_sender" required></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" required></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" required></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" required></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" required></p>
<p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" required></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_idTrue ]。

默认情况下,auto_id 设置为字符串 'id_%s'

Form.label_suffix

一个可翻译的字符串(英语中默认为冒号 (:)),在呈现表单时将附加在任何标签名称之后。

可以使用 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" required></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" required></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" required></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" required></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></li>

请注意,仅当标签的最后一个字符不是标点字符时才添加标签后缀(在英文中,它们是 .!?:)。

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

Form.use_required_attribute

当设置为 True(默认值)时,必填表单字段将具有 required HTML 属性。

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


配置表单小部件的呈现

Form.default_renderer

指定用于表单的 渲染器 。 默认为 None,这意味着使用由 :setting:`FORM_RENDERER` 设置指定的默认渲染器。

您可以在声明表单时将其设置为类属性,或使用 Form.__init__()renderer 参数。 例如:

from django import forms

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

或者:

form = MyForm(renderer=MyRenderer())

字段排序注意事项

as_p()as_ul()as_table() 快捷方式中,字段按您在表单类中定义的顺序显示。 例如,在 ContactForm 示例中,字段的定义顺序为 subjectmessagesendercc_myself。 要对 HTML 输出重新排序,请更改这些字段在类中的列出顺序。

还有其他几种自定义顺序的方法:

Form.field_order

默认情况下 Form.field_order=None,它保留您在表单类中定义字段的顺序。 如果 field_order 是字段名称列表,则字段按列表指定的顺序排列,其余字段根据默认顺序附加。 列表中的未知字段名称将被忽略。 这使得可以通过将子类中的字段设置为 None 来禁用它,而无需重新定义排序。

您还可以将 Form.field_order 参数用于 Form 以覆盖字段顺序。 如果 Form 定义了 field_order ,则在实例化 Form 时包含 field_order,则后者 field_order ] 将有优先权。

Form.order_fields(field_order)

您可以随时使用 order_fields()field_order 中的字段名称列表重新排列字段。


错误是如何显示的

如果你渲染一个绑定的 Form 对象,渲染动作将自动运行表单的验证(如果它还没有发生),并且 HTML 输出将包含验证错误作为 <ul class="errorlist"> 靠近场地。 错误消息的特定位置取决于您使用的输出方法:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data, auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" required></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" required></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" required></td></tr>
<tr><th>Cc myself:</th><td><input 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" required></li>
<li>Message: <input type="text" name="message" value="Hi there" required></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" required></li>
<li>Cc myself: <input 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" required></p>
<p>Message: <input type="text" name="message" value="Hi there" required></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" required></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>

自定义错误列表格式

默认情况下,表单使用 django.forms.utils.ErrorList 来格式化验证错误。 如果你想使用一个替代类来显示错误,你可以在构建时传递它:

>>> from django.forms.utils import ErrorList
>>> class DivErrorList(ErrorList):
...     def __str__(self):
...         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" required></p>
<p>Message: <input type="text" name="message" value="Hi there" required></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" required></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>

更精细的输出

as_p()as_ul()as_table() 方法是快捷方式——它们不是显示表单对象的唯一方式。

class BoundField

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

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

要检索单个 BoundField,请使用字段名称作为键在表单上使用字典查找语法:

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

要检索所有 BoundField 对象,请迭代表单:

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">

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

>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" required>

BoundField的属性

BoundField.auto_id
BoundField 的 HTML ID 属性。 如果 Form.auto_idFalse,则返回一个空字符串。
BoundField.data

此属性返回由小部件的 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': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> 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.field
来自这个 BoundField 包装的表单类的表单 Field 实例。
BoundField.form
BoundField 绑定到的 Form 实例。
BoundField.help_text
字段的 help_text
BoundField.html_name
将在小部件的 HTML name 属性中使用的名称。 它考虑了 prefix 的形式。
BoundField.id_for_label

使用此属性来呈现此字段的 ID。 例如,如果您在模板中手动构建 <label>(尽管 label_tag() 会为您执行此操作):

<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}

默认情况下,这将是以 id_ 为前缀的字段名称(上例中为“id_my_field”)。 您可以通过在字段的小部件上设置 attrs 来修改 ID。 例如,声明一个这样的字段:

my_field = forms.CharField(widget=forms.TextInput(attrs={'id': 'myFIELD'}))

并使用上面的模板,将呈现如下内容:

<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
BoundField.initial

使用 BoundField.initial 检索表单域的初始数据。 如果存在,它从 Form.initial 检索数据,否则尝试 Field.initial。 评估可调用值。 有关更多示例,请参阅 初始表单值

BoundField.initial 缓存它的返回值,这在处理返回值可以改变的可调用对象时特别有用(例如 datetime.nowuuid.uuid4):

>>> from datetime import datetime
>>> class DatedCommentForm(CommentForm):
...     created = forms.DateTimeField(initial=datetime.now)
>>> f = DatedCommentForm()
>>> f['created'].initial
datetime.datetime(2021, 7, 27, 9, 5, 54)
>>> f['created'].initial
datetime.datetime(2021, 7, 27, 9, 5, 54)

建议使用 BoundField.initial,而不是 get_initial_for_field()

BoundField.is_hidden
如果此 BoundField 的小部件被隐藏,则返回 True
BoundField.label
字段的 标签 。 这用于 label_tag()
BoundField.name

表单中此字段的名称:

>>> f = ContactForm()
>>> print(f['subject'].name)
subject
>>> print(f['message'].name)
message
BoundField.widget_type

3.1 版中的新功能。

返回包装字段的小部件的小写类名称,删除任何尾随 inputwidget。 这可用于构建布局取决于小部件类型的表单。 例如:

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


BoundField的方法

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

返回一个 HTML 字符串,用于将其表示为 <input type="hidden">

**kwargs 被传递给 as_widget()

这种方法主要在内部使用。 您应该改用小部件。

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

通过渲染传递的小部件来渲染字段,添加任何作为 attrs 传递的 HTML 属性。 如果未指定小部件,则将使用该字段的默认小部件。

only_initial 由 Django 内部使用,不应显式设置。

BoundField.css_classes(extra_classes=None)

当您使用 Django 的渲染快捷方式时,CSS 类用于指示必需的表单字段或包含错误的字段。 如果您手动渲染表单,则可以使用 css_classes 方法访问这些 CSS 类:

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes()
'required'

如果除了可能需要的错误和必需类之外,还想提供一些其他类,可以将这些类作为参数提供:

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes('foo bar')
'foo bar required'
BoundField.label_tag(contents=None, attrs=None, label_suffix=None)

要单独渲染表单域的标签标签,可以调用其 label_tag() 方法:

>>> f = ContactForm(data={'message': ''})
>>> print(f['message'].label_tag())
<label for="id_message">Message:</label>

您可以提供 contents 参数来替换自动生成的标签标签。 attrs 字典可能包含 <label> 标签的附加属性。

生成的 HTML 包括表单的 label_suffix(默认情况下为冒号),或者,如果设置,当前字段的 label_suffix。 可选的 label_suffix 参数允许您覆盖任何先前设置的后缀。 例如,您可以使用空字符串隐藏所选字段上的标签。 如果您需要在模板中执行此操作,您可以编写自定义过滤器以允许将参数传递给 label_tag

BoundField.value()

使用此方法渲染该字段的原始值,因为它会被 Widget 渲染:

>>> initial = {'subject': 'welcome'}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data={'subject': 'hi'}, initial=initial)
>>> print(unbound_form['subject'].value())
welcome
>>> print(bound_form['subject'].value())
hi


定制 BoundField

如果您需要访问有关模板中表单字段的一些附加信息,并且使用 Field 的子类还不够,还可以考虑自定义 BoundField

自定义表单字段可以覆盖 get_bound_field()

Field.get_bound_field(form, field_name)
采用 Form 的实例和字段名称。 访问模板中的字段时将使用返回值。 它很可能是 BoundField 子类的一个实例。

例如,如果您有一个 GPSCoordinatesField,并且希望能够访问有关模板中坐标的其他信息,则可以按如下方式实现:

class GPSCoordinatesBoundField(BoundField):
    @property
    def country(self):
        """
        Return the country the coordinates lie in or None if it can't be
        determined.
        """
        value = self.value()
        if value:
            return get_country_from_coordinates(value)
        else:
            return None

class GPSCoordinatesField(Field):
    def get_bound_field(self, form, field_name):
        return GPSCoordinatesBoundField(form, self, field_name)

现在,您可以使用 模板:Form.coordinates.country 在模板中访问该国家/地区。


将上传的文件绑定到表单

处理具有 FileFieldImageField 字段的表单比普通表单稍微复杂一些。

首先,为了上传文件,您需要确保 <form> 元素将 enctype 正确定义为 "multipart/form-data"

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

其次,在使用表单时,需要绑定文件数据。 文件数据与普通表单数据分开处理,因此当您的表单包含 FileFieldImageField 时,您将需要在绑定表单时指定第二个参数。 因此,如果我们扩展 ContactForm 以包含一个名为 mugshotImageField,我们需要绑定包含面部照片图像的文件数据:

# 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()

测试多部分表单

Form.is_multipart()

如果您正在编写可重用的视图或模板,您可能无法提前知道您的表单是否为多部分表单。 is_multipart() 方法告诉您表单是否需要多部分编码才能提交:

>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True

以下是如何在模板中使用它的示例:

{% if form.is_multipart %}
    <form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
    <form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>

子类化表单

如果您有多个共享字段的 Form 类,则可以使用子类化来消除冗余。

当您对自定义 Form 类进行子类化时,生成的子类将包括父类的所有字段,然后是您在子类中定义的字段。

在此示例中,ContactFormWithPriority 包含来自 ContactForm 的所有字段,以及一个附加字段 priorityContactForm 字段首先排序:

>>> class ContactFormWithPriority(ContactForm):
...     priority = forms.CharField()
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <input type="text" name="message" required></li>
<li>Sender: <input type="email" name="sender" required></li>
<li>Cc myself: <input type="checkbox" name="cc_myself"></li>
<li>Priority: <input type="text" name="priority" required></li>

可以对多个表单进行子类化,将表单视为混入。 在此示例中,BeatleForm 子类化 PersonFormInstrumentForm(按此顺序),其字段列表包括来自父类的字段:

>>> 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.as_ul())
<li>First name: <input type="text" name="first_name" required></li>
<li>Last name: <input type="text" name="last_name" required></li>
<li>Instrument: <input type="text" name="instrument" required></li>
<li>Haircut type: <input type="text" name="haircut_type" required></li>

通过在子类上将字段的名称设置为 None,可以声明性地删除从父类继承的 Field。 例如:

>>> 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.as_ul())
<li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" required></li>
<li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" required></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" required></li>
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" required></li>

前缀也可以在表单类上指定:

>>> class PersonForm(forms.Form):
...     ...
...     prefix = 'person'