在Django的视图中使用form对象的方法
在学习了关于Form类的基本知识后,你会看到我们如何把它用到视图中,取代contact()代码中不整齐的部分。一下示例说明了我们如何用forms框架重写contact():
#views.py fromdjango.shortcutsimportrender_to_response frommysite.contact.formsimportContactForm defcontact(request): ifrequest.method=='POST': form=ContactForm(request.POST) ifform.is_valid(): cd=form.cleaned_data send_mail( cd['subject'], cd['message'], cd.get('email','noreply@example.com'), ['siteowner@example.com'], ) returnHttpResponseRedirect('/contact/thanks/') else: form=ContactForm() returnrender_to_response('contact_form.html',{'form':form}) #contact_form.html <html> <head> <title>Contactus</title> </head> <body> <h1>Contactus</h1> {%ifform.errors%} <pstyle="color:red;"> Pleasecorrecttheerror{{form.errors|pluralize}}below. </p> {%endif%} <formaction=""method="post"> <table> {{form.as_table}} </table> <inputtype="submit"value="Submit"> </form> </body> </html>
看看,我们能移除这么多不整齐的代码!Django的forms框架处理HTML显示、数据校验、数据清理和表单错误重现。
尝试在本地运行。装载表单,先留空所有字段提交空表单;继而填写一个错误的邮箱地址再尝试提交表单;最后再用正确数据提交表单。(根据服务器的设置,当send_mail()被调用时,你将得到一个错误提示。而这是另一个问题。)
改变字段显示
你可能首先注意到:当你在本地显示这个表单的时,message字段被显示成``inputtype=”text”``,而它应该被显示成<``textarea``>。我们可以通过设置*widget*来修改它:
fromdjangoimportforms classContactForm(forms.Form): subject=forms.CharField() email=forms.EmailField(required=False) message=forms.CharField(**widget=forms.Textarea**)
forms框架把每一个字段的显示逻辑分离到一组部件(widget)中。每一个字段类型都拥有一个默认的部件,我们也可以容易地替换掉默认的部件,或者提供一个自定义的部件。
考虑一下Field类表现*校验逻辑*,而部件表现*显示逻辑*。
设置最大长度
一个最经常使用的校验要求是检查字段长度。另外,我们应该改进ContactForm,使subject限制在100个字符以内。为此,仅需为CharField提供max_length参数,像这样:
fromdjangoimportforms classContactForm(forms.Form): subject=forms.CharField(**max_length=100**) email=forms.EmailField(required=False) message=forms.CharField(widget=forms.Textarea)
选项min_length参数同样可用。
设置初始值
让我们再改进一下这个表单:为字subject段添加*初始值*:"Iloveyoursite!"(一点建议,但没坏处。)为此,我们可以在创建Form实体时,使用initial参数:
defcontact(request): ifrequest.method=='POST': form=ContactForm(request.POST) ifform.is_valid(): cd=form.cleaned_data send_mail( cd['subject'], cd['message'], cd.get('email',`'noreply@example.com`_'), [`'siteowner@example.com`_'], ) returnHttpResponseRedirect('/contact/thanks/') else: form=ContactForm( **initial={'subject':'Iloveyoursite!'}** ) returnrender_to_response('contact_form.html',{'form':form})
现在,subject字段将被那个句子填充。
请注意,传入*初始值*数据和传入数据以*绑定*表单是有区别的。最大的区别是,如果仅传入*初始值*数据,表单是unbound的,那意味着它没有错误消息。
自定义校验规则
假设我们已经发布了反馈页面了,email已经开始源源不断地涌入了。这里有一个问题:一些提交的消息只有一两个字,我们无法得知详细的信息。所以我们决定增加一条新的校验:来点专业精神,最起码写四个字,拜托。
我们有很多的方法把我们的自定义校验挂在Django的form上。如果我们的规则会被一次又一次的使用,我们可以创建一个自定义的字段类型。大多数的自定义校验都是一次性的,可以直接绑定到form类.
我们希望``message``字段有一个额外的校验,我们增加一个``clean_message()``方法到``Form``类:
fromdjangoimportforms classContactForm(forms.Form): subject=forms.CharField(max_length=100) email=forms.EmailField(required=False) message=forms.CharField(widget=forms.Textarea) defclean_message(self): message=self.cleaned_data['message'] num_words=len(message.split()) ifnum_words<4: raiseforms.ValidationError("Notenoughwords!") returnmessage
Django的form系统自动寻找匹配的函数方法,该方法名称以clean_开头,并以字段名称结束。如果有这样的方法,它将在校验时被调用。
特别地,clean_message()方法将在指定字段的默认校验逻辑执行*之后*被调用。(本例中,在必填CharField这个校验逻辑之后。)因为字段数据已经被部分处理,所以它被从self.cleaned_data中提取出来了。同样,我们不必担心数据是否为空,因为它已经被校验过了。
我们简单地使用了len()和split()的组合来计算单词的数量。如果用户输入字数不足,我们抛出一个forms.ValidationError型异常。这个异常的描述会被作为错误列表中的一项显示给用户。
在函数的末尾显式地返回字段的值非常重要。我们可以在我们自定义的校验方法中修改它的值(或者把它转换成另一种Python类型)。如果我们忘记了这一步,None值就会返回,原始的数据就丢失掉了。
指定标签
HTML表单中自动生成的标签默认是按照规则生成的:用空格代替下划线,首字母大写。如email的标签是"Email"。(好像在哪听到过?是的,同样的逻辑被用于模块(model)中字段的verbose_name值。我们在第五章谈到过。)
像在模块中做过的那样,我们同样可以自定义字段的标签。仅需使用label,像这样:
classContactForm(forms.Form): subject=forms.CharField(max_length=100) email=forms.EmailField(required=False,**label='Youre-mailaddress'**) message=forms.CharField(widget=forms.Textarea)