本文分为四个部分,首先是Web表单的基础知识,然后分 表单API 表单字段 以及 表单和字段验证 3个部分来详细说明。
Web表单的基础知识
HTML forms
在HTML中,表单是内部元素的集合…,允许访问者执行诸如输入文本,选择选项,操纵对象或控件等操作,然后将该信息发送回服务器。
除了它的元素,表单必须指定两件事:
where:输入数据之后的回调url
how:返回数据的http方法
一般表单中还会包含一些用户看不到的隐藏文本字段,Django用它来确定下一步该做什么。
当 这个页面元素被触发的时候,数据被发送到视图 /admin
GET and POST
GET和POST是处理表单时唯一使用的HTTP方法。
使用post提交的时候,返回django的登陆表单,浏览器绑定表单数据,对其进行编码以进行传输,将其发送到服务器,然后接受其响应。
get则相反,将提交的数据捆绑成一个字符串,并使用它来组成一个URL。URL包含必须发送数据的地址,以及数据键和值。如果您在Django文档中进行搜索,您可以看到该文档将生成表单的URL
https://docs.djangoproject.com/search/?q=forms&release=1
get和post请求通常用于不同的目的:
当我们希望更改系统状态的时候就使用post,get仅仅用于不影响系统状态的请求。
GET也不适用于密码表单,因为密码会出现在URL中,因此也会出现在浏览器历史记录和服务器日志中,所有这些都是纯文本形式。它既不适用于大量数据,也不适用于二进制数据,如图像。使用GET 管理表单请求的Web应用程序存在安全风险:攻击者可以很容易地模仿表单的请求以访问系统的敏感部分。 POST与Django的CSRF保护等其他保护措施相结合,可以更好地控制访问。
另一方面,GET适用于Web搜索表单之类的内容,因为表示GET请求的URL 可以轻松加入书签,共享或重新提交。
Django’s role in forms
处理表格是一项复杂的业务。考虑Django的管理员,其中可能需要准备多种不同类型的数据以便在表单中显示,呈现为HTML,使用方便的界面进行编辑,返回到服务器,验证和清理,然后保存或传递进一步处理。
Django的表单功能可以简化和自动化这项工作的大部分内容,并且也可以比大多数程序员在他们自己编写的代码中更安全地执行它。
Django处理表单中涉及的工作的三个不同部分:
- 准备和重组数据以使其为渲染做好准备
- 为数据创建HTML表单
- 接收和处理客户提交的表格和数据
Forms in Django
在Web应用程序的上下文中,“form”可能指HTML ,或生成它的Django Form,或提交时返回的结构化数据,或这些部分的端到端工作集合。
The Django Form class
一个 Form类描述一个表单并确定它是如何工作和展现出来的。
表单字段在浏览器中表示为HTML“小部件”(“widget”)——一种用户界面机制。每个字段类型都有一个适当的默认窗口小部件类,但是这些都可以根据需要重写。
Instantiating, processing, and rendering forms
在Django中渲染对象时,我们通常会:
在视图中获取它(例如,从数据库中获取它)
将它传递给模板上下文
使用模板变量将其扩展为HTML标记
Building a form
假设您要在网站上创建一个简单的表单,以获取用户的名称。你的模板中需要这样的东西:
<form action="/your-name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
这是一个非常简单的形式。在实践中,表单可能包含数十或数百个字段,其中许多字段可能需要预先填充,我们可能期望用户在结束操作之前多次通过编辑-提交循环。
我们可能需要在浏览器中进行一些验证,甚至在表单提交之前;我们可能希望使用更复杂的字段,允许用户从日历中选择日期等等。
此时,让Django为我们完成大部分工作要容易得多。
Building a form in Django
我们已经知道我们希望HTML表单看起来像什么。我们在Django中的出发点是:
forms.py
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
它定义了一个只有一个字段(your_name)的表单类。我们已经对字段应用了一个友好的标签,它将在呈现时出现在
字段的最大允许长度由max_length定义。这两件事。它在HTML 上设置maxlength=”100”(因此浏览器首先应该阻止用户输入超过该数量的字符)。它还意味着,当Django从浏览器接收到表单时,它将验证数据的长度。
表单实例有一个is_valid()方法,该方法为其所有字段运行验证例程。调用此方法时,如果所有字段都包含有效数据,则为:
- 返回True
- 将所有的表单数据收集在cleaned_data 这个字典属性里面
整个表单,第一次呈现时,将如下所示:
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required />
请注意,它不包含标记或提交按钮。我们必须在模板中提供这些。
The view
发送回Django网站的表单数据由视图处理,通常与发布表单的视图相同。这允许我们重用一些相同的逻辑。
要处理表单,我们需要将其实例化到我们想要发布的URL的视图中:
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
如果我们使用GET请求到达这个视图,它将创建一个空的表单实例,并将其放在要呈现的模板上下文中。这是我们第一次访问URL时可能发生的情况。
如果使用POST请求提交表单,视图将再次创建一个表单实例,并用来自请求的数据填充它:form = NameForm(request.POST),这称为“将数据绑定到表单”(现在它是一个绑定表单)。
我们调用表单的is_valid()方法;如果不为真,我们返回到模板和表单。这一次表单不再是空的(未绑定的),因此HTML表单将填充先前提交的数据,可以根据需要对其进行编辑和纠正。
如果is_valid()为真,我们现在将能够在其cleaned_data属性中找到所有经过验证的表单数据。我们可以使用这些数据更新数据库或进行其他处理,然后再向浏览器发送HTTP重定向,告诉它下一步要去哪里。
The template
我们的name.html模板不需要做太多工作。最简单的例子是:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
所有表单的字段及其属性将从Django的模板语言解压缩到HTML标记{{ form }}
HTML5输入类型和浏览器验证
如果您的表单包含URLField、EmailField或任何整数字段类型,Django将使用url、电子邮件和数字HTML5输入类型。默认情况下,浏览器可能会对这些字段应用自己的验证,这可能比Django的验证更严格。如果希望禁用此行为,请在表单标记上设置novalidate属性,或者在字段上指定另一个小部件,如TextInput。
现在我们有了一个可用的web表单,由Django表单描述,由视图处理,并呈现为HTML 。
这就是您需要开始的全部内容,但是表单框架提供了更多的信息。一旦您理解了上面描述的过程的基础,您就应该准备好理解表单系统的其他特性,并准备了解更多关于底层机制的知识。
More about Django Form classes
所有的表单类都创建为 django.forms.Form或者django.forms.ModelForm的子类。可以将ModelForm看作Form的子类。Form和ModelForm实际上从一个(私有)BaseForm类继承了公共功能,但是这个实现细节并不重要。
事实上,如果您的表单将用于直接添加或编辑Django模型,那么ModelForm可以为您节省大量的时间、精力和代码,因为它将从一个模型类构建一个表单,以及相应的字段和属性。
Bound and unbound form instances
未绑定的表单没有与之关联的数据。当呈现给用户时,它将是空的或将包含默认值。
绑定表单已经提交了数据,因此可以用来判断该数据是否有效。如果呈现的是无效的绑定表单,它可以包含内联错误消息,告诉用户要纠正哪些数据。
表单的is_bound属性将告诉您一个表单是否有绑定到它的数据。
More on fields
考虑一个比上面的小示例更有用的表单,我们可以用它在个人网站上实现“联系我”功能:
forms.py
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
我们早期的表单使用了一个字段,your_name,一个CharField。在本例中,我们的表单有四个字段:subject、message、sender和cc_myself。CharField、EmailField和BooleanField只是可用字段类型的三种;在表单字段(https://docs.djangoproject.com/en/2.0/ref/forms/fields/)中可以找到完整的列表。
Widgets
每个表单字段都有一个对应的小部件类,它对应于一个HTML表单小部件,比如。
在大多数情况下,字段将具有一个合理的默认小部件。例如,默认情况下,CharField将有一个TextInput小部件,它在HTML中生成一个。如果您需要,那么您应该在定义表单字段时指定适当的小部件,就像我们对消息字段所做的那样。
Field data
无论使用表单提交的数据是什么,一旦通过调用is_valid()成功验证了它(并且is_valid()返回为True),验证后的表单数据将在form.cleaned_data 字典。这些数据将被很好地转换为Python类型。
您仍然可以直接从request.POST访问未经验证的数据,但已验证的数据更好。
在上面的联系人表单示例中,cc_myself将是一个布尔值。同样,IntegerField和FloatField等字段分别将值转换为Python int和float。
以下是如何在处理此表单的视图中处理表单数据:
views.py
from django.core.mail import send_mail
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)
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
Working with form templates
{{ form.as_table }} will render them as table cells wrapped in tags
{{ form.as_p }} will render them wrapped in
tags
{{ form.as_ul }} will render them wrapped in
- tags
-
Note that you’ll have to provide the surrounding
or- elements yourself.
Here’s the output of {{ form.as_p }} for our ContactForm instance:
<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> <textarea name="message" id="id_message" required></textarea></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>
请注意,每个表单字段都有一个ID属性设置为id_,由附带的标签标记引用。这对于确保辅助技术(如屏幕阅读器软件)可以访问表单非常重要。您还可以自定义生成标签和ID的方式。
Rendering fields manually
我们不必让Django解压缩表单的字段; 如果我们愿意,我们可以手动完成(例如,允许我们对字段进行重新排序)。每个字段都可以作为表单的属性使用,并且在Django模板中将被适当地呈现。例如:{{ form.name_of_field }}
{{ form.non_field_errors }} <div class="fieldWrapper"> {{ form.subject.errors }} <label for="{{ form.subject.id_for_label }}">Email subject:</label> {{ form.subject }} </div> <div class="fieldWrapper"> {{ form.message.errors }} <label for="{{ form.message.id_for_label }}">Your message:</label> {{ form.message }} </div> <div class="fieldWrapper"> {{ form.sender.errors }} <label for="{{ form.sender.id_for_label }}">Your email address:</label> {{ form.sender }} </div> <div class="fieldWrapper"> {{ form.cc_myself.errors }} <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label> {{ form.cc_myself }} </div>
<div class="fieldWrapper"> {{ form.subject.errors }} {{ form.subject.label_tag }} {{ form.subject }} </div>
Rendering form error messages
当然,这种灵活性的代价是更多的工作。到目前为止,我们还不需要担心如何显示表单错误,因为这已经为我们做好了准备。在本例中,我们必须确保对每个字段和整个表单的所有错误进行处理.请注意表单顶部和模板查找每个字段的错误:{{ form.non_field_errors }}
使用显示表单错误列表,呈现为无序列表。这可能看起来像:{{ form.name_of_field.errors }}
<ul class="errorlist"> <li>Sender is required.</li> </ul>
该列表有一个CSS类,errorlist允许您设置其外观样式。如果您希望进一步自定义错误显示,可以循环显示错误:
{% if form.subject.errors %} <ol> {% for error in form.subject.errors %} <li><strong>{{ error|escape }}</strong></li> {% endfor %} </ol> {% endif %}
非字段错误(和/或在使用帮助程序时在表单顶部呈现的隐藏字段错误form.as_p())将使用其他类来呈现,nonfield以帮助将它们与特定于字段的错误区分开来。例如,看起来像:{{ form.non_field_errors }}
<ul class="errorlist nonfield"> <li>Generic validation error</li> </ul>
如果您为每个表单字段使用相同的HTML,则可以使用 循环依次循环遍历每个字段来减少重复代码:{% for %}
{% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} {% if field.help_text %} <p class="help">{{ field.help_text|safe }}</p> {% endif %} </div> {% endfor %}
有用的属性包括:{{ field }}
{{ field.label }}
该领域的标签,例如:Email , address{{ field.label_tag }}
字段的标签包含在适当的HTML<label for="id_email">Email address:</label>
{{ field.id_for_label }}
将用于此字段的ID(id_email在上面的示例中)。如果您手动构建标签,则可能需要使用此代替label_tag。例如,如果你有一些内联JavaScript并且想要避免硬编码字段的ID,它也很有用。{{ field.value }}
该字段的值。例如someone@example.com。{{ field.html_name }}
将在input元素的name字段中使用的字段的名称。如果已设置,则将表单前缀考虑在内。{{ field.help_text }}
与该字段关联的任何帮助文本{{ field.errors }}
输出一个-
包含与此字段对应的任何验证错误
{% for error in field.errors %}
{{ field.is_hidden }}
如果表单字段是隐藏字段,则此属性为True,否则为False。作为模板变量,它不是特别有用,但在下面这样的条件测试中比较有用:{% if field.is_hidden %} {# Do something special #} {% endif %}
{{ field.field }}
Field来自BoundField包装的表单类的实例。您可以使用它来访问 Field属性,例如 。{{ char_field.field.max_length }}Looping over hidden and visible fields
Django在表单上提供了两种方法,允许您独立地遍历隐藏和可见字段:hidden_fields()和 visible_fields()。
{# 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 %}
此示例不处理隐藏字段中的任何错误。通常,隐藏字段中的错误是形式篡改的标志,因为正常形式的交互不会改变它们。但是,您也可以轻松地为这些表单错误插入一些错误显示。
Reusable form templates
如果您的站点在多个位置对表单使用相同的呈现逻辑,则可以通过将表单的循环保存在独立模板中并使用该include标记在其他模板中重用它来减少重复:
# In your form template: {% include "form_snippet.html" %} # In form_snippet.html: {% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %}
如果传递给模板的表单对象在上下文中具有不同的名称,则可以使用 标记的with参数对其进行别名include:
{% include "form_snippet.html" with form=comment_form %}
参考链接:
https://docs.djangoproject.com/en/2.0/topics/forms/
表单API
Bound and unbound forms
一个Form要么是绑定数据的 要么是未绑定数据的。
如果是绑定了数据的,那么它就能够验证概数据并且将表单呈现为HTML,并且在HTML中呈现该数据。
如果是未绑定的,因为没有需要验证的数据,就无法进行验证。但是它仍然可以将空白的表单呈现为HTML
class Form[source]
要创建未绑定的Form实例,只需要实例化这个类即可:
>>> f = ContactForm()
如果要讲数据绑定到表单,请将数据作为字典的第一个参数传递给Form类的构造函数:
>>> data = {'subject': 'hello', ... 'message': 'Hi there', ... 'sender': 'foo@example.com', ... 'cc_myself': True} >>> f = ContactForm(data)
在这个字典里面,键是字段的名称,对应于Form类中的属性,值是您尝试验证的数据。
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
Using forms to validate data
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.']}
在此字典中,键是字段名称,值是表示错误消息的字符串列表。错误消息存储在列表中,因为字段可能有多个错误消息。
我们可以在不调用is_valid的情况下去访问errors,因为不管是你起调用is_valid或者获取errors,表单数据都会被验证。验证的例程只会被调用一次,不管你去获取errors或者调用is_valid多少次。这意味着如果验证具有副作用的话,这个副作用只会被触发一次。
Form.errors.as_data()
返回一个对原生的ValidationError实例的字段的映射:
>>> f.errors.as_data() {'sender': [ValidationError(['Enter a valid email address.'])], 'subject': [ValidationError(['This field is required.'])]}
在任何我们需要通过代码识别错误的时候都可使用这个方法。当给定的错误信息被呈现的时候,它允许我们去重写错误信息,或者在视图中写入我们的自定义逻辑。它也可以用于在自定义的格式中进行序列化信息。例如,as_json()依赖于as_data()。
需要这个as_data()方法是为了做向后兼容。先前的 ValidationError 实例在他们被渲染出的错误信息被添加到Form.errors字典的时候就消失了。
最理想的实现方式,Form.errors被存储在ValidationError实例中,并且使用具有as前缀的方法来渲染它们。但为了不破坏期望表单中呈现的错误消息的代码,必须反过来做。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()不会转义其输出。
Form.errors.get_json_data(escape_html = False)
将错误作为适合序列化为JSON的字典返回。 Form.errors.as_json()返回序列化的JSON,同时在序列化之前返回错误数据。
Form.add_error(field, error)
此方法允许从Form.clean()方法内部或从表单外部向特定字段添加错误,经常性应用于视图中。
该field参数是应该添加错误的字段的名称。如果它的值是None错误将被视为返回的非字段错误Form.non_field_errors()。所述error参数可以是一个简单的字符串,或优选的一个实例 ValidationError。
请注意,Form.add_error()自动从中删除相关字段 cleaned_data。
Form.has_error(field, code=None)
此方法返回一个布尔值,该布尔值指示字段是否具有特定的错误代码。如果代码为None,则如果该字段包含任何错误,就会返回True。
要检查 non-field errors 就需要使用NON_FIELD_ERRORS作为字段参数。
Form.non_field_errors()
此方法返回Form.errors 与特定字段无关的错误列表。这包括Form.clean()引发的ValidationErrors 和使用Form.add_error(None, “…”)添加的错误。
Behavior of unbound forms
在未绑定的表单上进行时数据验证是不合理的,未绑定的表单用来记录:
>>> f = ContactForm() >>> f.is_valid() False >>> f.errors {}
Dynamic initial values
Form.initial
使用initial在运行时声明表单字段的初始值。例如,您可能希望用当前会话的用户名填充用户名字段。
没有必要在表单中包含每个字段。例如:>>> f = ContactForm(initial={'subject': 'Hi there!'})
初始化和后来实例化时传入的值具有先后顺序:
>>> 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)
使用get_initial_for_field()检索表单字段初始化值。它从Form.initial和Field.initial检索数据。按照这个顺序,并计算任何可调用的初始值。
Checking which form data has changed
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()
如果用户提交的数据和我们在初始化的时候提供的数据不一致,has_changed()就会返回True。这个结果是通过对form每一个字段调用Field.has_changed()方法得到的。
Form.changed_data
返回有差异的字段名称列表。无差异的时候返回一个空的列表。
>>> f = ContactForm(request.POST, initial=data) >>> if f.has_changed(): ... print("The following fields changed: %s" % ", ".join(f.changed_data))
Accessing the fields from the form
Form.fields
我们可以通过fields属性获取Form实例的字段信息。
可以看出f.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>
我们可以更改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属性,因为此修改将会影响到ContactForm同一个Python进程中的所有的后续实例:
>>> 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>'
Accessing “clean” data
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': '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将会被清洗为字符串。
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即使您在定义时传递额外数据,也始终只包含在其中定义的字段的键 。
>>> 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将包括其所有字段的键和值 ,即使数据不包含某些可选字段的值也是如此。
>>> 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'}
Outputting forms as HTML
第二个任务就是把表单渲染为html,想做到这一点,简单的打印就可以了。
>>> 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里面就会显示该数据:
>>> 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>
Form.as_p()
as_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>
Form.as_ul()
as_ul()将表单呈现为一系列
- 标记,每个标记
- 包含一个字段。
-
>>> 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>
Form.as_table()
as_table()将表单输出为HTML
。>>> 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>
Styling required or erroneous form rows