表单 API¶
关于此文档
本文档涵盖了 Django 表单 API 的详细内容。您应该先阅读使用表单入门。
绑定和未绑定表单¶
Form
实例要么绑定到一组数据,要么未绑定。
如果它绑定到一组数据,则能够验证该数据并将表单呈现为 HTML,其中数据显示在 HTML 中。
如果它未绑定,则无法进行验证(因为没有要验证的数据!),但它仍然可以将空白表单呈现为 HTML。
要创建一个未绑定的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()
方法的需求是由于向后兼容性。以前,一旦它们的呈现错误消息添加到Form.errors
字典中,ValidationError
实例就会丢失。理想情况下,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
实例。有关定义表单错误的最佳实践,请参见 引发 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
*并且*您在实例化 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¶
您可以从其 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",
... "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'}
即使在定义Form
时传递了额外数据,cleaned_data
也*只*包含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
表单时,默认渲染使用以下方法和属性。
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()
方法都调用render方法。所有参数都是可选的,默认为
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()
¶
- 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()
'<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.template_name_table¶
as_table()
使用的模板。默认值:'django/forms/table.html'
。
- Form.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.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>
<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
和/或 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>
配置表单小部件的渲染¶
- Form.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 输出,请更改这些字段在类中列出的顺序。
还有几种其他的自定义顺序的方法
- 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)
<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>
自定义错误列表格式¶
- class ErrorList(initlist=None, error_class=None, renderer=None)[source]¶
默认情况下,表单使用
django.forms.utils.ErrorList
来格式化验证错误。ErrorList
是一个类似列表的对象,其中initlist
是错误列表。此外,此类具有以下属性和方法。- error_class¶
渲染错误列表时使用的 CSS 类。任何提供的类都将添加到默认的
errorlist
类。
- renderer¶
指定
ErrorList
使用的 渲染器。默认为None
,这意味着使用由FORM_RENDERER
设置指定的默认渲染器。
- template_name¶
调用
__str__
或render()
时使用的模板名称。默认为'django/forms/errors/list/default.html'
,它是'ul.html'
模板的代理。
- template_name_ul¶
调用
as_ul()
时使用的模板名称。默认为'django/forms/errors/list/ul.html'
。此模板使用<li>
标签渲染错误,并使用<ul>
包装,CSS 类由error_class
定义。
- render(template_name=None, context=None, renderer=None)¶
render 方法由
__str__
以及as_ul()
方法调用。所有参数都是可选的,默认为:
template_name
:template_name
属性返回的值。context
:get_context()
方法返回的值。renderer
:renderer
属性返回的值。
- as_text()¶
使用
template_name_text
定义的模板渲染错误列表。
- as_ul()¶
使用
template_name_ul
定义的模板渲染错误列表。
如果需要自定义错误的渲染方式,可以通过覆盖
template_name
属性来实现,或者更一般地通过覆盖默认模板来实现,另见 Overriding built-in form templates。
更细粒度的输出¶
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[source]¶
此
BoundField
的 HTML ID 属性。如果Form.auto_id
为False
,则返回空字符串。
- BoundField.data[source]¶
此属性返回通过小部件的
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[source]¶
一个 类似列表的对象,打印时显示为 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¶
表单
Field
实例,此BoundField
封装了来自表单类的实例。
- BoundField.form¶
此
BoundField
绑定到的Form
实例。
- BoundField.id_for_label[source]¶
使用此属性渲染此字段的 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[source]¶
使用
BoundField.initial
来检索表单字段的初始数据。如果存在,则从Form.initial
中检索数据,否则尝试Field.initial
。可调用值将被计算。更多示例,请参见 Initial form values。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
的小部件是隐藏的,则返回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[source]¶
- Django 5.0 新特性。
使用
BoundField.as_field_group()
渲染的模板名称。如果设置了
template_name
,则返回该属性的值;否则返回field_template_name
。
BoundField
的方法¶
- BoundField.as_field_group()¶
- Django 5.0 新特性。
使用默认值使用
BoundField.render()
渲染字段,这将渲染BoundField
,包括其标签、帮助文本和错误,使用模板的template_name
(如果设置了该属性),否则使用field_template_name
。
返回一个表示为
<input type="hidden">
的HTML字符串。**kwargs
传递给as_widget()
。此方法主要用于内部。您应该改用小部件。
- BoundField.as_widget(widget=None, attrs=None, only_initial=False)[source]¶
通过渲染传递的小部件来渲染字段,并添加作为
attrs
传递的任何HTML属性。如果没有指定小部件,则将使用字段的默认小部件。only_initial
由Django内部使用,不应显式设置。
- BoundField.css_classes(extra_classes=None)[source]¶
当您使用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, tag=None)[source]¶
使用
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)[source]¶
调用
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()[source]¶
使用此方法可以渲染此字段的原始值,就像它将由
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)[source]¶
接受
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
,我们需要绑定包含 mugshot 图片的文件数据。
# 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¶
您可以将多个 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"
...