• 表单验证


    本片开始一下表单的验证内容。

    比如我们要控制输入,不能空,不能过长,邮箱正确,密码强度等等。

    普遍的js前端验证

    简单说明,不是学习Django的重点。

    我们只需要修改上篇中的search_form.html模板,给submit添加一个客户端事件即可,修改如下

     1 <html>
     2 <head>
     3     <title>Search</title>
     4     <script>
     5         function test() {
     6             var key = document.getElementById('q');
     7             if (key != null && key.value.trim().length > 0) {
     8                 alert("search key is ok!");
     9                 return true;
    10             }
    11             else {
    12                 alert("search key is empty!");
    13                 return false;
    14             }
    15         }
    16     </script>
    17 </head>
    18 <body>
    19     {% if error %}
    20     <p style="color:red;">Please submit a search term</p>
    21     {% endif %}
    22     <!--<form action="/forms/search/" method="get">-->
    23     <form action="" method="get">
    24         <input type="text" id="q" name="q">
    25         <input type="submit" value="Search" onclick="return test();">
    26     </form>
    27 </body>
    28 </html>
    <input type="submit" value="Search" onclick="return test();">这里添加了客户端的点击事件,并返回校验结果,如果为False将阻止表单提交。

    简单的验证

    之前已经验证为空,则给出提示,那如果我们要控制检索词长度呢?显然根据error为True或False是不能区分那种错误的。我们修改视图函数如下

     1 def search(request):
     2     errors = []
     3     if 'q' in request.GET :
     4         q = request.GET['q']
     5         if not q:
     6             errors.append('Enter a search term.')
     7         elif len(q)>20:
     8             errors.append('enter most 20 charactors.')
     9         else:
    10             books = Book.objects.filter(title__icontains=q)
    11             return render_to_response('search_results.html',
    12             {'books': books, 'query': q})
    13 
    14     return render_to_response('search_form.html',{'errors':errors})

    将错误信息改为列表,为空时填写为空提示,过长时填写过长提示。

    因为模板参数变了,修改模板

     1 <body>
     2     {% if errors %}
     3     <ul>
     4         {% for error in errors %}
     5         <li><p style="color:red;">{{error}}</p></li>
     6         {% endfor %} 
     7     </ul>
     8     {% endif %}
     9     
    10     <form action="" method="get">
    11         <input type="text" id="q" name="q">
    12         <input type="submit" value="Search">
    13     </form>
    14 </body>
    15 </html>

    再看我们的结果

    虽然我们的目的达到了,但是如果有多种验证需求,我们要枚举所有验证,if else出来吗?显然这是不合理的。

    编写Contact表单

    我们从contact_form.html模板入手:

     1 <html>
     2 <head>
     3     <title>Contact us</title>
     4 </head>
     5 <body>
     6     <h1>Contact us</h1>
     7 
     8     {% if errors %}
     9     <ul>
    10         {% for error in errors %}
    11         <li>{{ error }}</li>
    12         {% endfor %}
    13     </ul>
    14     {% endif %}
    15 
    16     <form action="/contact/" method="post">
    17         {% csrf_token %}
    18         <p>Subject: <input type="text" name="subject"></p>
    19         <p>Your e-mail (optional): <input type="text" name="email"></p>
    20         <p>Message: <textarea name="message" rows="10" cols="50"></textarea></p>
    21         <input type="submit" value="Submit">
    22     </form>
    23 </body>
    24 </html>

    我们复制了前一个模板search_form.html中错误信息显示的代码。form的method设置为”post”而非”get”,因为这个表单提交之后会有一个服务器端的操作:发送一封e-mail。

    对应的contact视图如下:

     1 def contact(request):
     2     errors = []
     3     if request.method == 'POST':
     4         if not request.POST.get('subject', ''):
     5             errors.append('Enter a subject.')
     6         if not request.POST.get('message', ''):
     7             errors.append('Enter a message.')
     8         if request.POST.get('email') and '@' not in request.POST['email']:
     9             errors.append('Enter a valid e-mail address.')
    10         if not errors:
    11             try:
    12                 send_mail(
    13                     request.POST['subject'],
    14                     request.POST['message'],
    15                     request.POST.get('email', 'noreply@example.com'),
    16                     ['siteowner@example.com'],
    17                     )
    18             except:
    19                 return HttpResponse("exception.")
    20 
    21             return HttpResponseRedirect('/contact/thanks/')
    22     return render_to_response('contact_form.html',
    23         {'errors': errors},context_instance=RequestContext(request))

    除了必要的模块(比如HttpResponse,render_to_response等以前都提过),这里我们要新引入几个模块,如下

    1 from django.core.mail import send_mail
    2 from django.http import HttpResponseRedirect
    3 from django.template import Context,RequestContext

    这是我们这个contact视图用到的。(在写这些内容的时候,我遇到了些问题记录在contact表单错误解决记录,这里就不赘述了)

    由于视图要发送一封邮件,但是我们本地没有做更多邮件配置(参见这里),我们可以Console backend输出邮件信息。

    只需要在settings.py中增加如下一句即可:

    EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

    邮件发送成功需要转向thanks视图,如下

    1 def thanks(request):
    2     return HttpResponse("thanks for your suggestion!")

    我们使用HttpResponseRedirect对象将网页重定向至一个包含成功信息的页面,原因就是: 若用户刷新一个包含POST表单的页面,那么请求将会重新发送造成重复。 这通常会造成非期望的结果,比如说重复的数据库记录;在我们的例子中,将导致发送两封同样的邮件。 如果用户在POST表单之后被重定向至另外的页面,就不会造成重复的请求了。

    配置URLconf如下

    1 url(r'^contact/$',contact),
    2 url(r'^contact/thanks/$',thanks),

    我们的视图是可以正常运行的,运行效果就不贴了,contact表单错误解决记录里都有提到。

    当我们提交失败(email验证失效)后,数据全部丢失了!这显示是很糟糕的情况。我们修改视图函数,解决这个问题。

     1 def contact(request):
     2     errors = []
     3     if request.method == 'POST':
     4         if not request.POST.get('subject', ''):
     5             errors.append('Enter a subject.')
     6         if not request.POST.get('message', ''):
     7             errors.append('Enter a message.')
     8         if request.POST.get('email') and '@' not in request.POST['email']:
     9             errors.append('Enter a valid e-mail address.')
    10         if not errors:
    11             try:
    12                 send_mail(
    13                     request.POST['subject'],
    14                     request.POST['message'],
    15                     request.POST.get('email', 'noreply@example.com'),
    16                     ['siteowner@example.com'],
    17                     )
    18             except:
    19                 return HttpResponse("exception.")
    20 
    21             return HttpResponseRedirect('/contact/thanks/')
    22         
    23     return render_to_response('contact_form.html',
    24                               {'errors': errors,
    25                                'subject':request.POST.get('subject',''),
    26                                'message':request.POST.get('message',''),
    27                                'email':request.POST.get('email',''),
    28                                },
    29                               context_instance=RequestContext(request))

     我们在视图中增加了模型的参数,以便返回contact_form.html模型是能自动填充这些提交失败的数据。修改模型接收这些参数

     1 <html>
     2 <head>
     3     <title>Contact us</title>
     4 </head>
     5 <body>
     6     <h1>Contact us</h1>
     7 
     8     {% if errors %}
     9     <ul>
    10         {% for error in errors %}
    11         <li>{{ error }}</li>
    12         {% endfor %}
    13     </ul>
    14     {% endif %}
    15 
    16     <form action="/contact/" method="post">
    17         {% csrf_token %}
    18         <p>Subject: <input type="text" name="subject" value="{{subject}}"></p>
    19         <p>Your e-mail (optional): <input type="text" name="email" value="{{email}}"></p>
    20         <p>Message: <textarea name="message" rows="10" cols="50">{{message}}</textarea></p>
    21         <input type="submit" value="Submit">
    22     </form>
    23 </body>
    24 </html>

    这时候,当我们提交表单失败时,错误的数据会自动再次被填充到表单中,运行效果如下

    Form类

    直到这里,我们并没有解决如果有多种验证需求,我们要枚举所有验证,if else出来吗?的问题。下面使用Django自带的form库来解决这个问题,使用她来重写contact表单应用。

    小插曲

     Django的newforms库  

    在Django社区上会经常看到django.newforms这个词语。当人们讨论django.newforms,其实就是这里将要介绍的django.forms。  

    改名其实有历史原因的。 当Django一次向公众发行时,它有一个复杂难懂的表单系统:django.forms。后来它被完全重写了,新的版本改叫作:django.newforms,这样人们还可以通过名称,使用旧版本。 当Django 1.0发布时,旧版本django.forms就不再使用了,而django.newforms也终于可以名正言顺的叫做:django.forms。 

    Form类使用

    表单框架最主要的用法是,为每一个将要处理的HTML的 <Form> 定义一个Form类。 在这个例子中,我们只有一个 <Form> ,因此我们只需定义一个Form类。 这个类可以存在于任何地方,甚至直接写在 views.py 文件里也行,但是社区的惯例是把Form类都放到一个文件中:forms.py。在存放 views.py  的目录中,创建这个文件,然后输入:

    1 from django import forms
    2 
    3 class ContactForm(forms.Form):
    4     subject = forms.CharField()
    5     email = forms.EmailField(required=False)
    6     message = forms.CharField()

    这看上去简单易懂,很像在Django模型中使用的语法。

    表单中的每一个字段(域)作为Form类的属性,被展现成Field类。这里只用到CharFieldEmailField类型。 每一个字段都默认是必填。要使email成为可选项,我们需要指定required=False。

    让我们在交互式窗口看看这段代码做了些什么。

    记住,这里我们不能简单的使用Python命令进入交互模式,因为我们需要加载Django的运行环境,所以我们应该使用命令 

    Python manage.py shell

    认输出按照HTML的<table>标签格式,也可以是<ul>格式,如下:

    ContactForm类每个属性都默认生成input标签,name是类的属性名,id是'id_'+类的属性名。

    Form验证

    Form对象做的第二件事是校验数据。 为了校验数据,我们创建一个新的对Form象,并且传入一个与定义匹配的字典类型数据:

    f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice site!'})

    旦你对一个Form实体赋值,你就得到了一个绑定form,调用任何绑定form的is_valid()方法,就可以知道它的数据是否合法。如果我们不传入email值,它依然是合法的。因为我们指定这个字段的属性required=False,但是,如果留空subjectmessage,整个Form就不再合法了:

    你可以逐一查看每个字段及整个Form的出错消息:

    最终,如果一个Form实体的数据是合法的,它就会有一个可用的cleaned_data属性。 这是一个包含干净的提交数据的字典。 Django的form框架不但校验数据,它还会把它们转换成相应的Python类型数据,这叫做清理数据。

    在视图中使用Form对象

    我们修改之前的contact视图,使用Form对象来重写

     1 def contact(request):
     2     if request.method == 'POST':
     3         form = ContactForm(request.POST)
     4         if form.is_valid():
     5             cd=form.cleaned_data
     6             send_mail(
     7                 cd['subject'],
     8                 cd['message'],
     9                 cd.get('email', 'noreply@example.com'),
    10                     ['siteowner@example.com'],
    11                 )
    12             return HttpResponseRedirect('/contact/thanks/')
    13     else:
    14         form = ContactForm()
    15     return render_to_response('contact_form.html',{'form':form},context_instance=RequestContext(request))

    我们将各个字段使用ContactForm给包装起来了,初始化需要的参数刚好是包含在request.POST中。为什么呢?因为如果不是提交操作,我们返回contact_form.html模型,传递的参数是一个空的ContactForm,相当于给用户一个页面,让用户自己去填写初始化参数。

    再看我们修改的contact_form.html模型:

     1 <html>
     2 <head>
     3     <title>Contact us</title>
     4 </head>
     5 <body>
     6     <h1>Contact us</h1>
     7 
     8     {% if form.errors %}
     9         <p style="color:red;">please correct the error{{ form.errors|pluralize }} below</p>  
    10     {% endif %}
    11 
    12     <form action="" method="post">
    13         {% csrf_token %}
    14         <table>
    15             {{form.as_table}}
    16         </table>
    17         <input type="submit" value="Submit">
    18     </form>
    19 </body>
    20 </html>

    我们修改了errors,使用Forms自身的errors进行判定。页面的输入框使用form来自己填充,你可以自己选择是table格式或者ul格式。这里展示的结果就是ContactForm内定义的字段。

    我们看下运行效果:

    必填项为空的字段进行了提示,邮箱的验证也给出浮框提示,效果比自己写要漂亮很多。

    其他Form设置

    修改ContactForm如下

    1 class ContactForm(forms.Form):
    2     subject = forms.CharField(max_length=10)
    3     email = forms.EmailField(required=False, label='Your e-mail address' )
    4     message = forms.CharField(widget=forms.Textarea)

    我们为subject增加了最大长度校验,为email展示做了label修改,为message指定展示标签,效果如下

    设置初始值

     1 def contact(request):
     2     if request.method == 'POST':
     3         form = ContactForm(request.POST)
     4         if form.is_valid():
     5             cd=form.cleaned_data
     6             send_mail(
     7                 cd['subject'],
     8                 cd['message'],
     9                 cd.get('email', 'noreply@example.com'),
    10                     ['siteowner@example.com'],
    11                 )
    12             return HttpResponseRedirect('/contact/thanks/')
    13     else:
    14         form = ContactForm(initial={'subject': 'I love your site!'})
    15     return render_to_response('contact_form.html',{'form':form},context_instance=RequestContext(request))

    页面第一展示时,subject字段将被那个句子填充。

    传入初始值(initial) 数据和传入数据以绑定表单(new一个ContactForm对象)是有区别的。 最大的区别是,如果仅传入* 初始值* 数据,表单是unbound的,那意味着它没有错误消息。

    自定义校验规则

     1 from django import forms
     2 
     3 class ContactForm(forms.Form):
     4     subject = forms.CharField(max_length=100)
     5     email = forms.EmailField(required=False)
     6     message = forms.CharField(widget=forms.Textarea)
     7 
     8     def clean_message(self):
     9         message = self.cleaned_data['message']
    10         num_words = len(message.split())
    11         if num_words < 4:
    12             raise forms.ValidationError("Not enough words!")
    13         return message

    Django的form系统自动寻找匹配的函数方法,该方法名称以clean_开头,并以字段名称结束。 如果有这样的方法,它将在校验时被调用。clean_message()方法将在指定字段的默认校验逻辑执行之后被调用,比如默认的非空校验之后。

    定制Form设计

    修改contact_form.html如下,改动比较大

     1 <html>
     2 <head>
     3     <title>Contact us</title>
     4     <style type="text/css">
     5         ul.errorlist {
     6             margin: 0;
     7             padding: 0;
     8         }
     9 
    10         .errorlist li {
    11             background-color: red;
    12             color: white;
    13             display: block;
    14             font-size: 10px;
    15             margin: 0 0 3px;
    16             padding: 4px 5px;
    17         }
    18     </style>
    19 </head>
    20 <body>
    21     <h1>Contact us</h1>
    22 
    23     {% if form.errors %}
    24     <p style="color: red;">
    25         Please correct the error{{ form.errors|pluralize }} below.
    26     </p>
    27     {% endif %}
    28 
    29     <form action="" method="post">
    30         {% csrf_token %}
    31         <div class="field">
    32             {{ form.subject.errors }}
    33             <label for="id_subject">Subject:</label>
    34             {{ form.subject }}
    35         </div>
    36         <div class="field">
    37             {{ form.email.errors }}
    38             <label for="id_email">Your e-mail address:</label>
    39             {{ form.email }}
    40         </div>
    41         <div class="field{% if form.message.errors %} errors{% endif %}">
    42             {% if form.message.errors %}
    43             <ul>
    44                 {% for error in form.message.errors %}
    45                 <li><strong>{{ error }}</strong></li>
    46                 {% endfor %}
    47             </ul>
    48             {% endif %}
    49             <label for="id_message">Message:</label>
    50             {{ form.message }}
    51         </div>
    52         <input type="submit" value="Submit">
    53     </form>
    54 </body>
    55 </html>

    运行效果如下:

    我们看一下它生成的html

     1 <form action="" method="post">
     2         <input type="hidden" name="csrfmiddlewaretoken" value="3NfChix4d8Ttgqjc7mNfobwDYbY3HqWw">
     3         <div class="field">
     4             <ul class="errorlist"><li>确保该变量包含不超过 10 字符 (目前字符数 17)。</li></ul>
     5             <label for="id_subject">Subject:</label>
     6             <input id="id_subject" maxlength="10" name="subject" type="text" value="I love your site!">
     7         </div>
     8         <div class="field">
     9             <label for="id_email">Your e-mail address:</label>
    10             <input id="id_email" name="email" type="email">
    11         </div>
    12         <div class="field errors">
    13             <ul>
    14                 <li><strong>这个字段是必填项。</strong></li>
    15             </ul>
    16             
    17             <label for="id_message">Message:</label>
    18             <textarea cols="40" id="id_message" name="message" rows="10"></textarea>
    19         </div>
    20         <input type="submit" value="Submit">
    21     </form>

    {{ form.subject.errors }} 会在 <ul class="errorlist"> 里面显示。

    如果字段是合法的,或者form没有被绑定,就什么都不显示,如下是初始化时的html

     1 <form action="" method="post">
     2         <input type="hidden" name="csrfmiddlewaretoken" value="3NfChix4d8Ttgqjc7mNfobwDYbY3HqWw">
     3         <div class="field">
     4             <label for="id_subject">Subject:</label>
     5             <input id="id_subject" maxlength="10" name="subject" type="text" value="I love your site!">
     6         </div>
     7         <div class="field">
     8             <label for="id_email">Your e-mail address:</label>
     9             <input id="id_email" name="email" type="email">
    10         </div>
    11         <div class="field">
    12             <label for="id_message">Message:</label>
    13             <textarea cols="40" id="id_message" name="message" rows="10"></textarea>
    14         </div>
    15         <input type="submit" value="Submit">
    16     </form>

    小结

    这篇终于算是写完了,由于工作忙,一直没得空写完。东西比较多,还得慢慢消化才行。

    到这里,基础的初级知识基本就结束了。后边进入所谓‘高级阶段’,希望自己能继续坚持学习完并记录在这里。

  • 相关阅读:
    try {}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code 会不会被执行,什么时候被执行,在 return 前还是后?
    BigDecimal 使用 静态方法总结
    成员内部类里面为什么不能有静态成员和方法?
    浅谈多态机制的意义及实现
    Java接口中的成员变量的意义
    IDEA 打包和导入 Jar 包
    Java static关键字
    Java this关键字
    Java 匿名对象
    Java JOptionPane 对话框
  • 原文地址:https://www.cnblogs.com/cotton/p/3865976.html
Copyright © 2020-2023  润新知