本文共 17788 字,大约阅读时间需要 59 分钟。
关于这篇文档
这篇文档讲述Django 表单API 的详细细节。你应该先阅读。
要么是绑定的,要么是未绑定的。
class Form
若要创建一个未绑定的实例,只需简单地实例化该类:
>>> f = ContactForm()
若要绑定数据到表单,可以将数据以字典的形式传递给类的构造函数的第一个参数:
>>> data = { 'subject': 'hello',... 'message': 'Hi there',... 'sender': 'foo@example.com',... 'cc_myself': True}>>> f = ContactForm(data)
在这个字典中,键为字段的名称,它们对应于类中的属性。值为需要验证的数据。它们通常为字符串,但是没有强制要求必须是字符串;传递的数据类型取决于,我们稍后会看到。
Form.``is_bound
如果运行时刻你需要区分绑定的表单和未绑定的表单,可以检查下表单 属性的值:
>>> f = ContactForm()>>> f.is_boundFalse>>> f = ContactForm({ 'subject': 'hello'})>>> f.is_boundTrue
注意,传递一个空的字典将创建一个带有空数据的绑定的表单:
>>> f = ContactForm({})>>> f.is_boundTrue
如果你有一个绑定的实例但是想改下数据,或者你想绑定一个未绑定的表单到某些数据,你需要创建另外一个实例。 实例的数据没有办法修改。实例一旦创建,你应该将它的数据视为不可变的,无论它有没有数据。
Form.``clean
()
当你需要为相互依赖的字段添加自定义的验证时,你可以实现表单
的clean()
方法。示例用法参见。
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
访问 属性可以获得错误信息的一个字典:
>>> f.errors{ 'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
在这个字典中,键为字段的名称,值为表示错误信息的Unicode 字符串组成的列表。错误信息保存在列表中是因为字段可能有多个错误信息。
你可以在调用 之前访问。表单的数据将在第一次调用 或者访问 时验证。
验证将值调用一次,无论你访问 或者调用 多少次。这意味着,如果验证过程有副作用,这些副作用将只触发一次。
Form.errors.``as_data
()
New in Django 1.7.
返回一个字典
,它映射字段到原始的ValidationError
实例。
>>> f.errors.as_data(){ 'sender': [ValidationError(['Enter a valid email address.'])],'subject': [ValidationError(['This field is required.'])]}
每当你需要根据错误的code
来识别错误时,可以调用这个方法。它可以用来重写错误信息或者根据特定的错误编写自定义的逻辑。它还可以用来序列化错误为一个自定义的格式(例如,XML); 就依赖于as_data()
。
需要as_data()
方法是为了向后兼容。以前,ValidationError
实例在它们渲染后 的错误消息一旦添加到Form.errors
字典就立即被丢弃。理想情况下,Form.errors
应该已经保存ValidationError
实例而带有as_
前缀的方法可以渲染它们,但是为了不破坏直接使用Form.errors
中的错误消息的代码,必须使用其它方法来实现。
Form.errors.``as_json
(escape_html=False)
New in Django 1.7.
返回JSON 序列化后的错误。
>>> f.errors.as_json(){ "sender": [{ "message": "Enter a valid email address.", "code": "invalid"}],"subject": [{ "message": "This field is required.", "code": "required"}]}
默认情况下,as_json()
不会转义它的输出。如果你正在使用AJAX 请求表单视图,而客户端会解析响应并将错误插入到页面中,你必须在客户端对结果进行转义以避免可能的跨站脚本攻击。使用一个JavaScript 库比如jQuery 来做这件事很简单 —— 只要使用$(el).text(errorText)
而不是.html()
就可以。
如果由于某种原因你不想使用客户端的转义,你还可以设置escape_html=True
,这样错误消息将被转义而你可以直接在HTML 中使用它们。
Form.``add_error
(field, error)
New in Django 1.7.
这个方法允许在Form.clean()
方法内部或从表单的外部一起给字段添加错误信息;例如从一个视图中。
field
参数为字段的名称。如果值为None
,error 将作为 返回的一个非字段错误。
error
参数可以是一个简单的字符串,或者最好是一个ValidationError
实例。 中可以看到定义表单错误时的最佳实践。
注意,Form.add_error()
会自动删除cleaned_data
中的相关字段。
Form.``has_error
(field, code=None)
New in Django 1.8.
这个方法返回一个布尔值,指示一个字段是否具有指定错误code
的错误。当code
为None
时,如果字段有任何错误它都将返回True
。
若要检查非字段错误,使用 作为field
参数。
Form.``non_field_errors
()
这个方法返回 中不是与特定字段相关联的错误。它包含在 中引发的ValidationError
和使用 添加的错误。
验证没有绑定数据的表单是没有意义的,下面的例子展示了这种情况:
>>> f = ContactForm()>>> f.is_valid()False>>> f.errors{}
Form.``initial
表单字段的初始值使用声明。例如,你可能希望使用当前会话的用户名填充username
字段。
使用的参数可以实现。该参数是字段名到初始值的一个字典。只需要包含你期望给出初始值的字段;不需要包含表单中的所有字段。例如:
>>> f = ContactForm(initial={ 'subject': 'Hi there!'})
这些值只显示在没有绑定的表单中,即使没有提供特定值它们也不会作为后备的值。
注意,如果有定义, 而实例化表单
时也提供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)Name:Url:Comment:
Form.``has_changed
()
当你需要检查表单的数据是否从初始数据发生改变时,可以使用表单
的has_changed()
方法。
>>> data = { 'subject': 'hello',... 'message': 'Hi there',... 'sender': 'foo@example.com',... 'cc_myself': True}>>> f = ContactForm(data, initial=data)>>> f.has_changed()False
当提交表单时,我们可以重新构建表单并提供初始值,这样可以实现比较:
>>> f = ContactForm(request.POST, initial=data)>>> f.has_changed()
如果request.POST
中的数据与 中的不同,has_changed()
将为True
,否则为False
。 计算的结果是通过调用表单每个字段的 得到的。
Form.``fields
你可以从实例的fields
属性访问字段:
>>> for row in f.fields.values(): print(row)...>>> f.fields['name']
可你可以修改实例的字段来改变字段在表单中的表示:
>>> f.as_table().split('\n')[0]'Name:'>>> f.fields['name'].label = "Username">>> f.as_table().split('\n')[0]'Username:'
注意不要改变base_fields
属性,因为一旦修改将影响同一个Python 进程中接下来所有的ContactForm
实例:
>>> f.base_fields['name'].label = "Username">>> another_f = CommentForm(auto_id=False)>>> another_f.as_table().split('\n')[0]'Username:'
Form.``cleaned_data
类中的每个字段不仅负责验证数据,还负责“清洁”它们 —— 将它们转换为正确的格式。这是个非常好用的功能,因为它允许字段以多种方式输入数据,并总能得到一致的输出。
例如, 将输入转换为Python 的 datetime.date
对象。无论你传递的是'1994-07-15'
格式的字符串、datetime.date
对象、还是其它格式的数字,DateField
将始终将它们转换成datetime.date
对象,只要它们是合法的。
一旦你创建一个实例并通过验证后,你就可以通过它的cleaned_data
属性访问清洁的数据:
>>> data = { 'subject': 'hello',... 'message': 'Hi there',... 'sender': 'foo@example.com',... 'cc_myself': True}>>> f = ContactForm(data)>>> f.is_valid()True>>> f.cleaned_data{ 'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
注意,文本字段 —— 例如,CharField
和EmailField
—— 始终将输入转换为Unicode 字符串。我们将在这篇文档的后面将是编码的影响。
如果你的数据没有 通过验证,cleaned_data
字典中只包含合法的字段:
>>> data = { 'subject': '',... 'message': 'Hi there',... 'sender': 'invalid email address',... 'cc_myself': True}>>> f = ContactForm(data)>>> f.is_valid()False>>> f.cleaned_data{ 'cc_myself': True, 'message': 'Hi there'}
cleaned_data
始终只 包含表单
中定义的字段,即使你在构建表单
时传递了额外的数据。在下面的例子中,我们传递一组额外的字段给ContactForm
构造函数,但是cleaned_data
将只包含表单的字段:
>>> data = { 'subject': 'hello',... 'message': 'Hi there',... 'sender': 'foo@example.com',... 'cc_myself': True,... 'extra_field_1': 'foo',... 'extra_field_2': 'bar',... 'extra_field_3': 'baz'}>>> f = ContactForm(data)>>> f.is_valid()True>>> f.cleaned_data # Doesn't contain extra_field_1, etc.{ 'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
当表单
合法时,cleaned_data
将包含所有字段的键和值,即使传递的数据不包含某些可选字段的值。在下面的例子中,传递的数据字典不包含nick_name
字段的值,但是cleaned_data
任然包含它,只是值为空:
>>> from django.forms import Form>>> class OptionalPersonForm(Form):... first_name = CharField()... last_name = CharField()... nick_name = CharField(required=False)>>> data = { 'first_name': 'John', 'last_name': 'Lennon'}>>> f = OptionalPersonForm(data)>>> f.is_valid()True>>> f.cleaned_data{ 'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}
在上面的例子中,cleaned_data
中nick_name
设置为一个空字符串,这是因为nick_name
是CharField
而 CharField
将空值作为一个空字符串。每个字段都知道自己的“空”值 —— 例如,DateField
的空值是None
而不是一个空字符串。关于每个字段空值的完整细节,参见“内建的Field
类”一节中每个字段的“空值”提示。
你可以自己编写代码来对特定的字段(根据它们的名字)或者表单整体(考虑到不同字段的组合)进行验证。更多信息参见。
表单
对象的第二个任务是将它渲染成HTML。很简单,print
它:
>>> f = ContactForm()>>> print(f)
如果表单是绑定的,输出的HTML 将包含数据。例如,如果字段是<input type="text">
的形式,其数据将位于value
属性中。如果字段是<input type="checkbox">
的形式,HTML 将包含checked="checked"
:
>>> data = { 'subject': 'hello',... 'message': 'Hi there',... 'sender': 'foo@example.com',... 'cc_myself': True}>>> f = ContactForm(data)>>> print(f)
默认的输出时具有两个列的HTML 表格,每个字段对应一个<tr>
。注意事项:
<table>
和</table>
、<form>
和</form>
以及<input type="submit">
标签。你需要添加它们。CharField
表示为一个<input type="text">
,EmailField
表示为一个<input type="email">
。BooleanField
表示为一个<input type="checkbox">
。注意,这些只是默认的表示;你可以使用Widget 指定字段使用哪种HTML,我们将稍后解释。name
直接从ContactForm
类中获取。'Subject:'
、'Message:'
和'Cc myself:'
通过将所有的下划线转换成空格并大写第一个字母生成。再次提醒,这些只是默认的表示;你可以手工指定标签。<label>
标签,它指向表单字段的id
。这个id
,是通过在字段名称前面加上'id_'
前缀生成。id
属性和<label>
标签默认包含在输出中,但你可以改变这一行为。虽然print
表单时<table>
是默认的输出格式,但是还有其它格式可用。每个格式对应于表单对象的一个方法,每个方法都返回一个Unicode 对象。
Form.``as_p
()
as_p()
渲染表单为一系列的<p>
标签,每个<p>
标签包含一个字段:
>>> f = ContactForm()>>> f.as_p()'\n
\n
\n
'>>> print(f.as_p())
Form.``as_ul
()
as_ul()
渲染表单为一系列的<li>
标签,每个<li>
标签包含一个字段。它不包含<ul>
和</ul>
,所以你可以自己指定<ul>
的任何HTML 属性:
>>> f = ContactForm()>>> f.as_ul()'
Form.``as_table
()
最后,as_table()
输出表单为一个HTML <table>
。它与print
完全相同。事实上,当你print
一个表单对象时,在后台调用的就是as_table()
方法:
>>> f = ContactForm()>>> f.as_table()'\n\n\n'>>> print(f.as_table())
Form.``error_css_class
Form.``required_css_class
将必填的表单行和有错误的表单行定义不同的样式特别常见。例如,你想将必填的表单行以粗体显示、将错误以红色显示。
类具有一对钩子,可以使用它们来添加class
属性给必填的行或有错误的行:只需简单地设置 和/或 属性:
from django.forms import Formclass ContactForm(Form): error_css_class = 'error' required_css_class = 'required' # ... and the rest of your fields here
一旦你设置好,将根据需要设置行的"error"
和/或"required"
CSS 类型。 其HTML 看上去将类似:
>>> f = ContactForm(data)>>> print(f.as_table()) ... ... ...
Changed in Django 1.8:
required_css_class
添加到<label>
标签,如上面所看到的。
id
属性和 <label>
标签Form.``auto_id
默认情况下,表单的渲染方法包含:
id
属性<label>
标签。HTML <label>
标签指示标签文本关联的表单元素。这个小小的改进让表单在辅助设备上具有更高的可用性。使用<label>
标签始终是个好想法。id
属性值通过在表单字段名称的前面加上id_
生成。但是如果你想改变id
的生成方式或者完全删除 HTML id
属性和<label>
标签,这个行为是可配置的。
id
和label 的行为使用表单
构造函数的auto_id
参数控制。这个参数必须为True
、False
或者一个字符串。
如果auto_id
为False
,那么表单的输出将不包含<label>
标签和id
属性:
>>> f = ContactForm(auto_id=False)>>> print(f.as_table())Subject:Message:Sender:Cc myself:>>> print(f.as_ul())
Subject:
Message:
Sender:
Cc myself:
如果auto_id
设置为True
,那么输出的表示将 包含<label>
标签并简单地使用字典名称作为每个表单字段的id
:
>>> f = ContactForm(auto_id=True)>>> print(f.as_table())Subject: Message: Sender: Cc myself: >>> print(f.as_ul())
如果auto_id
设置为包含格式字符'%s'
的字符串,那么表单的输出将包含<label>
标签,并将根据格式字符串生成id
属性。例如,对于格式字符串'field_%s'
,名为subject
的字段的id
值将是'field_subject'
。继续我们的例子:
>>> f = ContactForm(auto_id='id_for_%s')>>> print(f.as_table())Subject: Message: Sender: Cc myself: >>> print(f.as_ul())
如果auto_id
设置为任何其它的真值 —— 例如不包含%s
的字符串 —— 那么其行为将类似auto_id
等于True
。
默认情况下,auto_id
设置为'id_%s'
。
Form.``label_suffix
一个字符串(默认为英文的:
),表单渲染时将附加在每个label 名称的后面。
使用label_suffix
参数可以自定义这个字符,或者完全删除它:
>>> f = ContactForm(auto_id='id_for_%s', label_suffix='')>>> print(f.as_ul())
注意,该标签后缀只有当label 的最后一个字符不是表单符号(.
, !
, ?
和:
)时才添加。
New in Django 1.8.
字段可以定义自己的。而且将优先于。在运行时刻,后缀可以使用 的label_suffix
参数覆盖。
在as_p()
、as_ul()
和as_table()
中,字段以表单类中定义的顺序显示。例如,在ContactForm
示例中,字段定义的顺序为subject
, message
, sender
, cc_myself
。若要重新排序HTML 中的输出,只需改变字段在类中列出的顺序。
如果你渲染一个绑定的表单
对象,渲染时将自动运行表单的验证,HTML 输出将在出错字段的附近以<ul class="errorlist">
形式包含验证的错误。错误信息的位置与你使用的输出方法有关:
>>> data = {'subject': '',... 'message': 'Hi there',... 'sender': 'invalid email address',... 'cc_myself': True}>>> f = ContactForm(data, auto_id=False)>>> print(f.as_table())Subject:
Subject:
Message:
Sender:
Cc myself:
默认情况下,表单使用django.forms.utils.ErrorList
来格式化验证时的错误。如果你希望使用另外一种类来显示错误,可以在构造时传递(在Python 2 中将 __str__
替换为__unicode__
):
>>> from django.forms.utils import ErrorList>>> class DivErrorList(ErrorList):... def __str__(self): # __unicode__ on Python 2... return self.as_divs()... def as_divs(self):... if not self: return ''... return '%s' % ''.join(['%s' % e for e in self])>>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)>>> f.as_p()This field is required.Subject:
Message:
Enter a valid email address.Sender:
Cc myself:
Changed in Django 1.7:
django.forms.util
重命名为django.forms.utils
。
as_p()
、as_ul()
和as_table()
方法是为懒惰的程序员准备的简单快捷方法 —— 它们不是显示表单的唯一方式。
class BoundField
用于显示HTML 表单或者访问实例的一个属性。
其__str__()
(Python 2 上为__unicode__
)方法显示该字段的HTML。
以字段的名称为键,用字典查询语法查询表单,可以获取一个 BoundField
:
>>> form = ContactForm()>>> print(form['subject'])
迭代表单可以获取所有的BoundField
:
>>> form = ContactForm()>>> for boundfield in form: print(boundfield)
字段的输出与表单的auto_id
设置有关:
>>> f = ContactForm(auto_id=False)>>> print(f['message'])>>> f = ContactForm(auto_id='id_%s')>>> print(f['message'])
若要获取字段的错误列表,可以访问字段的errors
属性。
BoundField.``errors
一个类列表对象,打印时以HTML <ul class="errorlist">
形式显示:
>>> data = { 'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}>>> f = ContactForm(data, auto_id=False)>>> print(f['message'])>>> f['message'].errors['This field is required.']>>> print(f['message'].errors)
BoundField.``label_tag
(contents=None, attrs=None, label_suffix=None)
可以调用label_tag
方法单独渲染表单字段的label 标签:
>>> f = ContactForm(data)>>> print(f['message'].label_tag())Message:
如果你提供一个可选的contents
参数,它将替换自动生成的label 标签。另外一个可选的attrs
参数可以包含<label>
标签额外的属性。
生成的HTML 包含表单的(默认为一个冒号),或者当前字段的。可选的label_suffix
参数允许你覆盖之前设置的后缀。例如,你可以使用一个空字符串来隐藏已选择字段的label。如果在模板中需要这样做,你可以编写一个自定义的过滤器来允许传递参数给label_tag
。
Changed in Django 1.8:
如果可用,label 将包含。
BoundField.``css_classes
()
当你使用Django 的快捷的渲染方法时,习惯使用CSS 类型来表示必填的表单字段和有错误的字段。如果你是手工渲染一个表单,你可以使用css_classes
方法访问这些CSS 类型:
>>> f = ContactForm(data)>>> f['message'].css_classes()'required'
除了错误和必填的类型之外,如果你还想提供额外的类型,你可以用参数传递它们:
>>> f = ContactForm(data)>>> f['message'].css_classes('foo bar')'foo bar required'
BoundField.``value
()
这个方法用于渲染字段的原始值,与用Widget
渲染的值相同:
>>> initial = { 'subject': 'welcome'}>>> unbound_form = ContactForm(initial=initial)>>> bound_form = ContactForm(data, initial=initial)>>> print(unbound_form['subject'].value())welcome>>> print(bound_form['subject'].value())hi
BoundField.``id_for_label
使用这个属性渲染字段的ID。例如,如果你在模板中手工构造一个<label>
(尽管 将为你这么做):
... { { my_field }}
默认情况下,它是在字段名称的前面加上id_
(上面的例子中将是“id_my_field
”)。你可以通过设置字段Widget 的 来修改ID。例如,像这样声明一个字段:
my_field = forms.CharField(widget=forms.TextInput(attrs={ 'id': 'myFIELD'}))
使用上面的模板,将渲染成:
...
处理带有FileField
和ImageField
字段的表单比普通的表单要稍微复杂一点。
首先,为了上传文件,你需要确保你的<form>
元素正确定义enctype
为"multipart/form-data"
:
其次,当你使用表单时,你需要绑定文件数据。文件数据的处理与普通的表单数据是分开的,所以如果表单包含FileField
和ImageField
,绑定表单时你需要指定第二个参数。所以,如果我们扩展ContactForm 并包含一个名为mugshot
的ImageField
,我们需要绑定包含mugshot 图片的文件数据:
# Bound form with an image field>>> from django.core.files.uploadedfile import SimpleUploadedFile>>> data = { 'subject': 'hello',... 'message': 'Hi there',... 'sender': 'foo@example.com',... 'cc_myself': True}>>> file_data = { 'mugshot': SimpleUploadedFile('face.jpg',)}>>> 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
()
如果你正在编写可重用的视图或模板,你可能事先不知道你的表单是否是一个multipart 表单。is_multipart()
方法告诉你表单提交时是否要求multipart:
>>> f = ContactFormWithMugshot()>>> f.is_multipart()True
下面是如何在模板中使用它的一个示例:
{% if form.is_multipart %}
如果你有多个表单
类共享相同的字段,你可以使用子类化来减少冗余。
当你子类化一个自定义的表单
类时,生成的子类将包含父类中的所有字段,以及在子类中定义的字段。
在下面的例子中,ContactFormWithPriority
包含ContactForm
中的所有字段,以及另外一个字段priority
。排在前面的是ContactForm
中的字段:
>>> class ContactFormWithPriority(ContactForm):... priority = forms.CharField()>>> f = ContactFormWithPriority(auto_id=False)>>> print(f.as_ul())
可以子类化多个表单,将表单作为“mix-ins”。在下面的例子中,BeatleForm
子类化PersonForm
和 InstrumentForm
,所以它的字段列表包含两个父类的所有字段:
>>> from django.forms import Form>>> class PersonForm(Form):... first_name = CharField()... last_name = CharField()>>> class InstrumentForm(Form):... instrument = CharField()>>> class BeatleForm(PersonForm, InstrumentForm):... haircut_type = CharField()>>> b = BeatleForm(auto_id=False)>>> print(b.as_ul())
New in Django 1.7.
* 在子类中,可以通过设置名字为None
来删除从父类中继承的字段
。例如: ```>>> from django import forms>>> class ParentForm(forms.Form):... name = forms.CharField()... age = forms.IntegerField()>>> class ChildForm(ParentForm):... name = None>>> ChildForm().fields.keys()... ['age']```
Form.``prefix
你可以将几个Django 表单放在一个<form>
标签中。为了给每个表单
一个自己的命名空间,可以使用prefix
关键字参数:
>>> mother = PersonForm(prefix="mother")>>> father = PersonForm(prefix="father")>>> print(mother.as_ul())
译者:,原文:。
本文以 协议发布,转载请保留作者署名和文章出处。
人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。