表单 API¶
关于此文档
此文档涵盖 Django 表单 API 的具体细节。您应首先阅读 使用表单的简介。
绑定和未绑定表单¶
一个 Form
实例要么绑定到一组数据,要么未绑定。
- 如果它绑定到一组数据,它能够验证该数据,并将表单呈现为 HTML,其中数据显示在 HTML 中。
- 如果它未绑定,它不能进行验证(因为没有要验证的数据!),但它仍然可以将空白表单呈现为 HTML。
-
class
Form
¶
要创建一个未绑定的 Form
实例,实例化该类
>>> f = ContactForm()
要将数据绑定到表单,将数据作为字典作为 Form
类构造函数的第一个参数传递
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
在此字典中,键是字段名称,它对应于 Form
类中的属性。值是您尝试验证的数据。这些通常是字符串,但没有要求它们必须是字符串;您传递的数据类型取决于 Field
,我们稍后会看到。
-
Form.
is_bound
¶
如果您需要在运行时区分绑定和未绑定表单实例,请检查表单的 is_bound
属性的值
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({"subject": "hello"})
>>> f.is_bound
True
请注意,传递一个空字典会创建一个具有空数据的绑定表单
>>> f = ContactForm({})
>>> f.is_bound
True
如果您有一个绑定的 Form
实例并希望以某种方式更改数据,或者如果您希望将未绑定的 Form
实例绑定到某些数据,请创建另一个 Form
实例。没有办法更改 Form
实例中的数据。一旦创建了 Form
实例,您应该考虑其数据不可变,无论它是否有数据。
使用表单验证数据¶
-
Form.
clean
()¶
当您必须为相互依赖的字段添加自定义验证时,在 Form
上实现一个 clean()
方法。请参阅 验证和清理相互依赖的字段 以获取示例用法。
-
Form.
is_valid
()¶
一个 Form
对象的主要任务是验证数据。使用一个绑定的 Form
实例,调用 is_valid()
方法来运行验证并返回一个布尔值,表示数据是否有效
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
我们尝试使用一些无效数据。在这种情况下,subject
为空(一个错误,因为默认情况下所有字段都是必需的),sender
不是一个有效的电子邮件地址
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
-
Form.
errors
¶
访问 errors
属性以获取一个错误消息字典
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
在这个字典中,键是字段名称,值是表示错误消息的字符串列表。错误消息存储在列表中,因为一个字段可以有多个错误消息。
你可以访问 errors
而无需先调用 is_valid()
。第一次调用 is_valid()
或访问 errors
时,表单数据将被验证。
无论你访问 errors
或调用 is_valid()
多少次,验证例程只会被调用一次。这意味着如果验证有副作用,这些副作用只会触发一次。
-
Form.errors.
as_data
()¶
返回一个 dict
,它将字段映射到其原始的 ValidationError
实例。
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
在需要通过 code
识别错误时,随时使用此方法。当出现给定错误时,这将启用诸如重写错误消息或在视图中编写自定义逻辑之类的功能。它还可用于以自定义格式(例如 XML)序列化错误;例如,as_json()
依赖于 as_data()
。
需要 as_data()
方法是出于向后兼容性。以前,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,而这在序列化之前返回错误数据。
在 Form.errors.as_json()
中,escape_html
参数的行为如所述。
-
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
字段。
要实现此目的,请对 Form
使用 initial
参数。如果给出了此参数,它应该是一个将字段名称映射到初始值的字典。仅包含你指定初始值的字段;不必包含表单中的每个字段。例如
>>> f = ContactForm(initial={"subject": "Hi there!"})
这些值仅显示在未绑定表单中,并且如果未提供特定值,则不会将它们用作备用值。
如果 Field
定义了 initial
并且在实例化 Form
时包含 initial
,那么后者的 initial
将具有优先权。在此示例中,initial
在字段级别和表单实例级别同时提供,并且后者具有优先权
>>> from django import forms
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial="class")
... url = forms.URLField()
... comment = forms.CharField()
...
>>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" value="instance" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
-
Form.
get_initial_for_field
(field, field_name)¶
返回表单字段的初始数据。如果存在,则从 Form.initial
中检索数据,否则尝试 Field.initial
。可调用值将被评估。
建议使用 BoundField.initial
而非 get_initial_for_field()
,因为 BoundField.initial
具有更简单的界面。此外,与 get_initial_for_field()
不同,BoundField.initial
会缓存其值。这非常有用,尤其是在处理可调用值时,其返回值可能发生变化(例如 datetime.now
或 uuid.uuid4
)
>>> import uuid
>>> class UUIDCommentForm(CommentForm):
... identifier = forms.UUIDField(initial=uuid.uuid4)
...
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
检查哪些表单数据已更改¶
-
Form.
has_changed
()¶
当需要检查表单数据是否已从初始数据更改时,请在 Form
上使用 has_changed()
方法。
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "cc_myself": True,
... }
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False
当表单提交时,我们对其进行重构并提供原始数据,以便进行比较
>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()
has_changed()
如果 request.POST
中的数据与 initial
中提供的数据不同,则为 True
,否则为 False
。结果通过为表单中的每个字段调用 Field.has_changed()
来计算。
-
Form.
changed_data
¶
changed_data
属性返回一个字段名称列表,这些字段在表单绑定数据(通常为 request.POST
)中的值与 initial
中提供的值不同。如果没有数据不同,则返回一个空列表。
>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
... print("The following fields changed: %s" % ", ".join(f.changed_data))
...
>>> f.changed_data
['subject', 'message']
从表单访问字段¶
-
Form.
fields
¶
你可以从 Form
实例的 fields
属性访问其字段
>>> for row in f.fields.values():
... print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields["name"]
<django.forms.fields.CharField object at 0x7ffaac6324d0>
你可以更改 BoundField
字段和 Form
实例,以更改其在表单中的显示方式
>>> 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 = CommentForm(auto_id=False)
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
访问“干净”数据¶
-
Form.
cleaned_data
¶
Form
类中的每个字段不仅负责验证数据,还负责“清理”数据,即将其标准化为一致的格式。这是一个很好的特性,因为它允许以多种方式输入特定字段的数据,始终产生一致的输出。
例如,DateField
将输入标准化为 Python datetime.date
对象。无论你是否传递给它格式为 '1994-07-15'
的字符串、datetime.date
对象或其他格式,只要有效,DateField
始终会将其标准化为 datetime.date
对象。
一旦你使用一组数据创建了 Form
实例并对其进行了验证,你就可以通过其 cleaned_data
属性访问干净的数据
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': '[email protected]', 'subject': 'hello'}
请注意,任何基于文本的字段(例如 CharField
或 EmailField
)始终将输入清理为字符串。我们将在本文档后面部分介绍编码含义。
如果数据不验证,cleaned_data
字典仅包含有效字段
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}
cleaned_data
仅始终包含 Form
中定义的字段的键,即使在定义 Form
时传递额外数据也是如此。在此示例中,我们向 ContactForm
构造函数传递大量额外字段,但 cleaned_data
仅包含表单的字段
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "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': '[email protected]', 'subject': 'hello'}
当 Form
有效时,cleaned_data
将包含其所有字段的键和值,即使数据未包含某些可选字段的值也是如此。在此示例中,数据字典不包含 nick_name
字段的值,但 cleaned_data
包含它,且值为一个空值
>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
... nick_name = forms.CharField(required=False)
...
>>> data = {"first_name": "John", "last_name": "Lennon"}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}
在此上述示例中,nick_name
的 cleaned_data
值设置为一个空字符串,因为 nick_name
是 CharField
,而 CharField
将空值视为一个空字符串。每个字段类型都知道其“空白”值是什么 - 例如,对于 DateField
,它是 None
,而不是空字符串。有关每种字段在这种情况下行为的完整详细信息,请参阅下方“内置 Field
类”部分中每种字段的“空值”注释。
您可以编写代码来对特定表单字段(基于其名称)或整个表单(考虑各种字段的组合)执行验证。有关此内容的更多信息,请参阅表单和字段验证。
将表单输出为 HTML¶
Form
对象的第二个任务是将自身呈现为 HTML。为此,print
它
>>> f = ContactForm()
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself"></div>
如果表单绑定到数据,则 HTML 输出将适当地包含该数据。例如,如果一个字段由 <input type="text">
表示,则数据将位于 value
属性中。如果一个字段由 <input type="checkbox">
表示,则该 HTML 将在适当情况下包含 checked
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" value="hello" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" value="Hi there" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" value="[email protected]" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></div>
此默认输出用 <div>
包装每个字段。请注意以下内容
- 为了灵活性,输出不包含
<form>
和</form>
标记或<input type="submit">
标记。这是您的工作。 - 每个字段类型都有一个默认的 HTML 表示形式。
CharField
由<input type="text">
表示,EmailField
由<input type="email">
表示。BooleanField(null=False)
由<input type="checkbox">
表示。请注意,这些只是明智的默认值;你可以使用小组件指定给定字段要使用的 HTML,我们稍后会对此进行说明。 - 每个标签的 HTML
name
直接取自ContactForm
类中的属性名称。 - 每个字段的文本标签,例如
'Subject:'
、'Message:'
和'Cc myself:'
是通过将所有下划线转换为空格并将第一个字母大写来从字段名称生成的。同样,请注意,这些只是明智的默认值;你也可以手动指定标签。 - 每个文本标签都包含在 HTML
<label>
标签中,该标签通过其id
指向相应的表单字段。反过来,其id
是通过在字段名称前加上'id_'
生成的。id
属性和<label>
标签默认包含在输出中,以遵循最佳实践,但你可以更改该行为。 - 输出使用 HTML5 语法,目标是
<!DOCTYPE html>
。例如,它使用布尔属性,如checked
,而不是 XHTML 样式的checked='checked'
。
虽然 <div>
输出是你 print
表单时的默认输出样式,但你可以通过使用自己的表单模板自定义输出,该模板可以在整个网站、每个表单或每个实例中设置。请参见 可重用表单模板。
默认呈现¶
当你 print
表单时的默认呈现使用以下方法和属性。
template_name
¶
-
Form.
template_name
¶
如果表单被强制转换为字符串,则呈现模板的名称,例如通过 print(form)
或通过 {{ form }}
在模板中。
默认情况下,返回呈现器的 form_template_name
的值。你可以将它设置为字符串模板名称,以便为特定的表单类覆盖它。
render()
¶
-
Form.
render
(template_name=None, context=None, renderer=None)¶
渲染方法由 __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
值。
注意
在框架提供的模板和输出样式中,建议使用默认 as_div()
,而不是 as_p()
、as_table()
和 as_ul()
版本,因为该模板实现了 <fieldset>
和 <legend>
来对相关输入进行分组,并且屏幕阅读器用户更容易导航。
每个帮助器将表单方法与提供适当模板名称的属性配对。
as_div()
¶
-
Form.
template_name_div
¶
as_div()
使用的模板。默认值:'django/forms/div.html'
。
-
Form.
as_div
()¶
as_div()
将表单呈现为一系列 <div>
元素,每个 <div>
包含一个字段,例如
>>> f = ContactForm()
>>> f.as_div()
… 会生成类似这样的 HTML
<div>
<label for="id_subject">Subject:</label>
<input type="text" name="subject" maxlength="100" required id="id_subject">
</div>
<div>
<label for="id_message">Message:</label>
<input type="text" name="message" required id="id_message">
</div>
<div>
<label for="id_sender">Sender:</label>
<input type="email" name="sender" required id="id_sender">
</div>
<div>
<label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
</div>
as_p()
¶
-
Form.
template_name_p
¶
as_p()
使用的模板。默认值:'django/forms/p.html'
。
-
Form.
as_p
()¶
as_p()
将表单呈现为一系列 <p>
标记,每个 <p>
包含一个字段
>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
as_ul()
¶
-
表单。
template_name_ul
¶
as_ul()
使用的模板。默认值:'django/forms/ul.html'
。
-
表单。
as_ul
()¶
as_ul()
将表单呈现为一系列 <li>
标签,每个 <li>
包含一个字段。它不包含 <ul>
或 </ul>
,以便你可以灵活地在 <ul>
上指定任何 HTML 属性
>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
as_table()
¶
-
表单。
template_name_table
¶
as_table()
使用的模板。默认值:'django/forms/table.html'
。
-
表单。
as_table
()¶
as_table()
将表单呈现为 HTML <table>
>>> f = ContactForm()
>>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f)
<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>
为必需或错误的表单行设置样式¶
-
表单。
error_css_class
¶
-
表单。
required_css_class
¶
对必需的或有错误的表单行和字段进行样式设置非常常见。例如,您可能希望以粗体显示必需的表单行,并以红色突出显示错误。
表单
类有一些挂钩,您可以使用它们向必需的行或有错误的行添加 class
属性:设置 Form.error_css_class
和/或 Form.required_css_class
属性
from django import forms
class ContactForm(forms.Form):
error_css_class = "error"
required_css_class = "required"
# ... and the rest of your fields here
完成此操作后,将根据需要向行提供 "error"
和/或 "required"
类。HTML 将类似于
>>> f = ContactForm(data)
>>> print(f)
<div class="required"><label for="id_subject" class="required">Subject:</label> ...
<div class="required"><label for="id_message" class="required">Message:</label> ...
<div class="required"><label for="id_sender" class="required">Sender:</label> ...
<div><label for="id_cc_myself">Cc myself:</label> ...
>>> f["subject"].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f["subject"].legend_tag()
<legend class="required" for="id_subject">Subject:</legend>
>>> f["subject"].label_tag(attrs={"class": "foo"})
<label for="id_subject" class="foo required">Subject:</label>
>>> f["subject"].legend_tag(attrs={"class": "foo"})
<legend for="id_subject" class="foo required">Subject:</legend>
配置表单元素的 HTML id
属性和 <label>
标记¶
-
表单。
auto_id
¶
默认情况下,表单呈现方法包括
- 表单元素上的 HTML
id
属性。 - 标签周围相应的
<label>
标记。HTML<label>
标记指定哪个标签文本与哪个表单元素关联。此小增强使表单更易于使用,并且更易于辅助设备访问。始终建议使用<label>
标记。
通过在表单字段名称前添加 id_
来生成 id
属性值。不过,如果你想更改 id
约定或完全删除 HTML id
属性和 <label>
标记,则可以配置此行为。
使用 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>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></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="sender">Sender:</label><input type="email" name="sender" required id="sender"></div>
<div><label for="cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="cc_myself"></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_sender">Sender:</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></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_sender">Sender</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
>>> 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_sender">Sender -></label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself -></label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
请注意,仅当标签的最后一个字符不是标点符号(在英语中,这些字符是 .
、!
、?
或 :
)时,才会添加标签后缀。
字段还可以定义自己的 label_suffix
。这将优先于 Form.label_suffix
。还可以使用 label_suffix
参数覆盖 label_tag()
/ legend_tag()
中的运行时后缀。
-
表单。
use_required_attribute
¶
当设置为 True
(默认值)时,必需的表单字段将具有 required
HTML 属性。
表单集 使用 use_required_attribute=False
实例化表单,以避免在从表单集中添加和删除表单时出现不正确的浏览器验证。
配置表单小部件的呈现¶
-
表单。
default_renderer
¶
指定用于表单的 渲染器。默认为 None
,表示使用 FORM_RENDERER
设置指定的默认渲染器。
在声明表单时,可以将此设置为类属性,或将 renderer
参数用于 Form.__init__()
。例如
from django import forms
class MyForm(forms.Form):
default_renderer = MyRenderer()
或
form = MyForm(renderer=MyRenderer())
字段排序说明¶
在 as_p()
、as_ul()
和 as_table()
快捷方式中,字段按在表单类中定义它们的顺序显示。例如,在 ContactForm
示例中,字段按 subject
、message
、sender
、cc_myself
的顺序定义。要重新排序 HTML 输出,请更改类中列出这些字段的顺序。
还有其他几种自定义顺序的方法
-
表单。
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)
<div>Subject:
<ul class="errorlist"><li>This field is required.</li></ul>
<input type="text" name="subject" maxlength="100" required aria-invalid="true">
</div>
<div>Message:
<textarea name="message" cols="40" rows="10" required>Hi there</textarea>
</div>
<div>Sender:
<ul class="errorlist"><li>Enter a valid email address.</li></ul>
<input type="email" name="sender" value="invalid email address" required aria-invalid="true">
</div>
<div>Cc myself:
<input type="checkbox" name="cc_myself" checked>
</div>
自定义错误列表格式¶
-
类
ErrorList
(initlist=无, error_class=无, renderer=无)¶ 默认情况下,表单使用
django.forms.utils.ErrorList
来格式化验证错误。ErrorList
是一个类似列表的对象,其中initlist
是错误列表。此外,此类具有以下属性和方法。-
error_class
¶ 呈现错误列表时要使用的 CSS 类。任何提供的类都会添加到默认的
errorlist
类中。
-
renderer
¶ 指定 渲染器 用于
ErrorList
。默认为无
,这意味着使用FORM_RENDERER
设置指定的默认渲染器。
-
template_name
¶ 调用
__str__
或render()
时使用的模板的名称。默认情况下,这是'django/forms/errors/list/default.html'
,它是'ul.html'
模板的代理。
-
template_name_text
¶ 调用
as_text()
时使用的模板名称。默认情况下,此名称为'django/forms/errors/list/text.html'
。此模板将错误渲染为项目符号列表。
-
template_name_ul
¶ 调用
as_ul()
时使用的模板名称。默认情况下,此名称为'django/forms/errors/list/ul.html'
。此模板在<li>
标记中渲染错误,并使用<ul>
进行包装,其中 CSS 类由error_class
定义。
-
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()
方法是快捷方式——它们并不是显示表单对象唯一的方式。
要检索单个 BoundField
,请使用字段名称作为键在表单上使用字典查找语法
>>> form = ContactForm()
>>> print(form["subject"])
<input id="id_subject" type="text" name="subject" maxlength="100" required>
要检索所有 BoundField
对象,请迭代表单
>>> form = ContactForm()
>>> for boundfield in form:
... print(boundfield)
...
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
特定于字段的输出遵循表单对象的 auto_id
设置
>>> f = ContactForm(auto_id=False)
>>> print(f["message"])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id="id_%s")
>>> print(f["message"])
<input type="text" name="message" id="id_message" required>
BoundField
的属性¶
-
BoundField.
auto_id
¶ 此
BoundField
的 HTML ID 属性。如果Form.auto_id
为False
,则返回一个空字符串。
-
BoundField.
data
¶ 此属性返回此
BoundField
的数据,由小部件的value_from_datadict()
方法提取,如果未给出,则为None
>>> unbound_form = ContactForm() >>> print(unbound_form["subject"].data) None >>> bound_form = ContactForm(data={"subject": "My Subject"}) >>> print(bound_form["subject"].data) My Subject
-
BoundField.
errors
¶ 一个 类列表的对象,打印时显示为 HTML
<ul class="errorlist">
>>> data = {"subject": "hi", "message": "", "sender": "", "cc_myself": ""} >>> f = ContactForm(data, auto_id=False) >>> print(f["message"]) <input type="text" name="message" required aria-invalid="true"> >>> f["message"].errors ['This field is required.'] >>> print(f["message"].errors) <ul class="errorlist"><li>This field is required.</li></ul> >>> f["subject"].errors [] >>> print(f["subject"].errors) >>> str(f["subject"].errors) ''
在呈现带有错误的字段时,
aria-invalid="true"
将在字段的小组件上设置,以指示屏幕阅读器用户存在错误。在 Django 5.0 中更改当字段出现错误时,添加了
aria-invalid="true"
。
-
BoundField.
field
¶ 此
BoundField
封装的表单类中的表单Field
实例。
-
BoundField.
form
¶ 此
BoundField
绑定的Form
实例。
-
BoundField.
id_for_label
¶ 使用此属性来呈现此字段的 ID。例如,如果你正在模板中手动构造一个
<label>
(尽管label_tag()
/legend_tag()
会为你执行此操作)<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
默认情况下,这将是字段名称,其前缀为
id_
(对于上面的示例为 ”id_my_field
”)。你可以通过设置字段小部件上的attrs
来修改 ID。例如,像这样声明一个字段my_field = forms.CharField(widget=forms.TextInput(attrs={"id": "myFIELD"}))
并使用上面的模板,将呈现类似于
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
-
BoundField.
initial
¶ 使用
BoundField.initial
来检索表单字段的初始数据。如果存在,它将从Form.initial
检索数据,否则尝试Field.initial
。可调用值将被评估。有关更多示例,请参阅 初始表单值。BoundField.initial
会缓存其返回值,这非常有用,尤其是在处理返回值可能更改的可调用对象(例如datetime.now
或uuid.uuid4
)时>>> from datetime import datetime >>> class DatedCommentForm(CommentForm): ... created = forms.DateTimeField(initial=datetime.now) ... >>> f = DatedCommentForm() >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54) >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54)
建议使用
BoundField.initial
,而不是get_initial_for_field()
。
如果此
BoundField
的小部件是隐藏的,则返回True
。
-
BoundField.
label
¶ 字段的
label
。这用于label_tag()
/legend_tag()
。
-
BoundField.
name
¶ 此字段在表单中的名称
>>> f = ContactForm() >>> print(f["subject"].name) subject >>> print(f["message"].name) message
-
BoundField.
template_name
¶ - Django 5.0 中的新增功能。
使用
BoundField.as_field_group()
渲染的模板的名称。返回
template_name
值的属性(如果已设置),否则返回field_template_name
。
-
BoundField.
use_fieldset
¶ 返回此 BoundField 小部件的
use_fieldset
属性的值。
-
BoundField.
widget_type
¶ 返回包装字段小部件的小写类名,并删除任何尾随的
input
或widget
。当构建布局依赖于小部件类型时,可以使用此方法。例如{% for field in form %} {% if field.widget_type == 'checkbox' %} # render one way {% else %} # render another way {% endif %} {% endfor %}
BoundField
的方法¶
-
BoundField.
as_field_group
()¶ - Django 5.0 中的新增功能。
使用
BoundField.render()
渲染字段,其中包含默认值,该值会渲染BoundField
,包括其标签、帮助文本和错误,如果已设置,则使用模板的template_name
,否则使用field_template_name
返回一个 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.
get_context
()¶ - Django 5.0 中的新增功能。
返回用于呈现字段的模板上下文。可用上下文为
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
。
提示
在模板中,
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)¶ 使用
<legend>
标记调用label_tag()
,其中tag='legend'
,以使用<legend>
标记呈现标签。在呈现单选按钮和小组框小组件时,这很有用,其中<legend>
可能比<label>
更合适。
-
BoundField.
render
(template_name=None, context=None, renderer=None)¶ - Django 5.0 中的新增功能。
render 方法由
as_field_group
调用。所有参数都是可选的,并且默认为template_name
:BoundField.template_name
context
:BoundField.get_context()
返回的值renderer
:Form.default_renderer
返回的值
通过传递
template_name
,你可以自定义仅用于单个调用的模板。
-
BoundField.
value
()¶ 使用此方法可呈现此字段的原始值,就像
Widget
呈现的那样>>> initial = {"subject": "welcome"} >>> unbound_form = ContactForm(initial=initial) >>> bound_form = ContactForm(data={"subject": "hi"}, initial=initial) >>> print(unbound_form["subject"].value()) welcome >>> print(bound_form["subject"].value()) hi
自定义 BoundField
¶
如果你需要在模板中访问有关表单字段的一些其他信息,并且使用 Field
的子类还不够,请考虑同时自定义 BoundField
。
自定义表单字段可以覆盖 get_bound_field()
-
Field.
get_bound_field
(form, field_name)¶ 获取
Form
的一个实例和字段的名称。在模板中访问字段时将使用返回值。它很可能是BoundField
子类的实例。
例如,如果你有一个 GPSCoordinatesField
,并且希望能够在模板中访问有关坐标的其他信息,则可以按如下方式实现
class GPSCoordinatesBoundField(BoundField):
@property
def country(self):
"""
Return the country the coordinates lie in or None if it can't be
determined.
"""
value = self.value()
if value:
return get_country_from_coordinates(value)
else:
return None
class GPSCoordinatesField(Field):
def get_bound_field(self, form, field_name):
return GPSCoordinatesBoundField(form, self, field_name)
现在,你可以使用 {{ form.coordinates.country }}
在模板中访问国家。
将上传的文件绑定到表单¶
处理具有 FileField
和 ImageField
字段的表单比普通表单复杂一些。
首先,为了上传文件,你需要确保你的 <form>
元素正确地将 enctype
定义为 "multipart/form-data"
<form enctype="multipart/form-data" method="post" action="/foo/">
其次,当你使用表单时,你需要绑定文件数据。文件数据与普通表单数据分开处理,因此当你的表单包含 FileField
和 ImageField
时,你需要在绑定表单时指定第二个参数。因此,如果我们扩展我们的 ContactForm 以包含一个名为 mugshot
的 ImageField
,我们需要绑定包含头像图像的文件数据
# Bound form with an image field
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "[email protected]",
... "cc_myself": True,
... }
>>> file_data = {"mugshot": SimpleUploadedFile("face.jpg", b"file data")}
>>> f = ContactFormWithMugshot(data, file_data)
在实践中,你通常会将 request.FILES
指定为文件数据源(就像你使用 request.POST
作为表单数据源一样)
# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)
构建一个未绑定的表单与往常一样——省略表单数据和文件数据
# Unbound form with an image field
>>> f = ContactFormWithMugshot()
测试多部分表单¶
-
Form.
is_multipart
()¶
如果你正在编写可重用的视图或模板,你可能无法预先知道你的表单是否是多部分表单。 is_multipart()
方法告诉你表单是否需要多部分编码才能提交
>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True
以下是如何在模板中使用它的示例
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>
子类化表单¶
如果你有多个共享字段的 Form
类,你可以使用子类化来消除冗余。
当你对自定义 Form
类进行子类化时,生成的子类将包含父类的所有字段,然后是你在子类中定义的字段。
在此示例中, ContactFormWithPriority
包含 ContactForm
中的所有字段,以及一个附加字段 priority
。 ContactForm
字段首先排序
>>> class ContactFormWithPriority(ContactForm):
... priority = forms.CharField()
...
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
<div>Priority:<input type="text" name="priority" required></div>
可以对多个表单进行子类化,将表单视为 mixin。在此示例中, 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>
通过将子类的字段名称设置为 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
¶
可以在一个 <form>
标签中放置多个 Django 表单。要为每个 Form
提供自己的命名空间,请使用 prefix
关键字参数
>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother)
<div><label for="id_mother-first_name">First name:</label><input type="text" name="mother-first_name" required id="id_mother-first_name"></div>
<div><label for="id_mother-last_name">Last name:</label><input type="text" name="mother-last_name" required id="id_mother-last_name"></div>
>>> print(father)
<div><label for="id_father-first_name">First name:</label><input type="text" name="father-first_name" required id="id_father-first_name"></div>
<div><label for="id_father-last_name">Last name:</label><input type="text" name="father-last_name" required id="id_father-last_name"></div>
也可以在表单类中指定前缀
>>> class PersonForm(forms.Form):
... ...
... prefix = "person"
...