关于这篇文档
这篇文档提供Django表单处理的入门。 如需要参看更多关于表单的API及细节,请参照 The Forms API, Form fields, 和 :doc:`/ref/forms/validation`。
django.forms 是Django表单控制的库
表单的提交由:class:~django.http.HttpRequest 类处理,你可以使用表单库来处理常用的表单任务:
1.使用表单小工具自动生成HTML表单。 2.使用验证规则验证提交的表单内容。 3.呈现表单验证错误。 4.把提交的表单数据类型转换为所依赖的Python数据类型。
表单库包含这些概念
这个库与其它Django组件,类似数据库层、视图及模板层解耦。该库只依赖Django的设置,以及一些``django.utils``的帮助程序以及Django的国际化内容(你也可以不使用国际化内容)。
一个表单对象封装了一系列的表单字段及验证规则。使用``django.forms.Form``创建表单对象,创建的过程很像你使用Django的数据库对象那样。
例如,以下表单实现了“与我联系”功能:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
表单是由``Field`` 组合而成的。在这个例子里,我们有四个字段:``subject``,``message``,``sender``和``cc_myself``。``CharField``,``EmailField``以及``BooleanField``是用于验证的字段类型。你可从这里查看全部列表: :doc:`/ref/forms/fields`。
如果你希望直接在表单中编辑Django中声明的对象,你可以使用:doc:ModelForm </topics/forms/modelforms> 去避免重复定义你的对象。
在视图中使用表单的标准流程是这样的:
def contact(request):
if request.method == 'POST': # 如果表单提交
form = ContactForm(request.POST) # 绑定数据到表单
if form.is_valid(): # 验证所有规则
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Post提交后跳转
else:
form = ContactForm() # 空表单
return render_to_response('contact.html', {
'form': form,
})
这段代码有三个分支:
1.如果表单尚未提交,应该创建一个未绑定的ContactForm对象并发到模板。 2.如果表单已被提交,就使用 ``request.POST``创建表单对象。如果提交的数据都通过验证,就把跳转到“thanks”页面。 3.如果表单已被提交,但不通过验证,就把绑定的表单对象发送到模板。
表单中**绑定**和**未绑定**非常重要。未绑定的表单没有任何与它关联的数据;当呈现到用户面前时,表单会是空的,或是只有些默认数据。一个绑定的表单具有提交的数据,可视为数据室有效的。如果一个未能通过验证的表单会呈现一条错误消息,告知用户表单报错。
参考:ref:`ref-forms-api-bound-unbound`获取更多关于绑定和未绑定表单的信息。
当 ``is_valid()``方法返回 ``True``,你就可以安全地处理符合表单验证规则的数据。虽然这时你可以使用 ``request.POST``取得数据,但使用``form.cleaned_data``更好。这里的数据不单止已经验证过了,而且还会转换为对应的Python对象。在上面的例子中,``cc_myself``就是布尔类型的。同样地,像``IntegerField``和``FloatField``转换为了Python中的整形和浮点型。要注意的是,只读字段并不会在``form.cleaned_data``中(就算是使用自定义``clean()``方法也不会有任何作用),因为这些字段被呈现为text而不是input元素,所以不会提交回服务器。
以下代码扩展了上面的例子,用于说明如何处理表单数据:
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['info@example.com']
if cc_myself:
recipients.append(sender)
from django.core.mail import send_mail
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/') # 提交后跳转
参阅:doc:`/topics/email`获得更多关于使用Django发送邮件的信息。
表单天生就设计为可用于Django模板语言。在上面的例子中,我们把``ContactForm``对象发送到模板中的``form``变量中。这里是一个模板例子:
<form action="/contact/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
表单会输出它的字段;``<form>`` 标签及提交按钮就由你自己决定要如何呈现。
表单及跨站请求保护
Django提供了一套易用的:doc:`protection against Cross Site Request Forgeries </ref/contrib/csrf>`。当使用POST提交表单,你必须使用 :ttag:`csrf_token`模板标签以启用CSRF保护你的表单。本文档接下来的例子中,将暂时省略在表单中使用CSRF。
``form.as_p``会输出表单中的所有字段以及它的说明文本。以下是一个模板的例子:
<form action="/contact/" method="post">
<p><label for="id_subject">Subject:</label>
<input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label>
<input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
<input type="text" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
</form>
需要注意的是,每个表单字段的ID属性都设置为``id_<field-name>``,这些将会被文本标签引用。这对一些帮助阅读系统非常重要,就像是一些读屏软件。你也可以:ref:`customize the way in which labels and ids are generated <ref-forms-api-configuring-label>`。
你也可以使用``form.as_table``把表单输出为表格(你需要自己提供``<table>``标签)或使用``form.as_ul``输出为列表。
如果你不喜欢生成的默认表单HTML,你完全可以使用Django模板语言自定义你的表单要如何呈现。以下室扩展示例:
<form action="/contact/" method="post">
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="id_subject">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="id_message">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="id_sender">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="id_cc_myself">CC yourself?</label>
{{ form.cc_myself }}
</div>
<p><input type="submit" value="Send message" /></p>
</form>
每个表单字段可以用``{{ form.name_of_field }}``输出到模板里,这样就可以呈现出表单元素了。使用``{{ form.name_of_field.errors }}``可以呈现出未排序的表单的错误列表,将会这样:
<ul class="errorlist">
<li>Sender is required.</li>
</ul>
列表中的``errorlist``CSS类让你可更改列表的样式。 如果你希望再进一步自定义呈现错误列表,你也可以像这样使用循环:
{% if form.subject.errors %}
<ol>
{% for error in form.subject.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
你可以使用``{% for %}``循环来减少使用相同的代码来呈现表单元素:
<form action="/contact/" method="post">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
</form>
在循环里,``{{ field }}``是:class:`BoundField`的实例。 ``BoundField``有如下属性,在模板里非常有用:
``{{ field.label }}``
字段的文本说明,譬如``电子邮件地址``。
如把该属性设置为``True``,字段将为隐藏字段,反之亦然。这在模板中并不太实用,但在测试时你可以这样:
{% if field.is_hidden %}
{# Do something special #}
{% endif %}
如果你不依赖Django默认布局,而手动重新布局,而希望处理``<input type=”hidden”>``于其它可见标签与众不同。譬如,因为隐藏字段不会显示任何东西,把错误消息放在字段的“旁边”可能会引起用户的疑惑,所以字段的错误消息应该已不同的方式处理。
Django提供了两种方法来遍历隐藏字段和可见字段:``hidden_fields()``和``visible_fields()``。这里是我们对上面的例子使用这两个方法的一些修改:
<form action="/contact/" method="post">
{# Include the hidden fields #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{# Include the visible fields #}
{% for field in form.visible_fields %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
</form>
在这个例子里我们没有使用隐藏字段来控制错误信息。通常错误信息是表单篡改的标志,用户交互不会有任何改变。当啊,你可以把错误信息更友好地显示出来。
如果你的网站在多个地方都用相同的表单呈现逻辑,你可以使用:ttag:`include`标签来减少每个模板的循环代码:
<form action="/contact/" method="post">
{% include "form_snippet.html" %}
<p><input type="submit" value="Send message" /></p>
</form>
# In form_snippet.html:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
如果表单元素使用模板上下文中不同的名称,你可以使用:ttag:`include`的 ``with``参数设置别名:
<form action="/comments/add/" method="post">
{% include "form_snippet.html" with form=comment_form %}
<p><input type="submit" value="Submit comment" /></p>
</form>
如果你发现你会重复做这样的事,你应该考虑使用自定义标签:ref:`inclusion tag<howto-custom-template-tags-inclusion-tags>`。
This covers the basics, but forms can do a whole lot more:
See also
Dec 14, 2013