the django travel three[form表单验证]
一:表单验证:
场景:因为浏览器的js可以被禁用,所以需要做后台的输入合法的验证。
A:ajax发请求。需要注意的是ajax POST的数据的key值和form表单的里的字段名字一致,否则得不到验证!!!
通过ajax发请求给后端提交数据,然后后端通过form进行输入的验证。
之前ajax POST数据的时候后端需要做如下:
1 def login(request): 2 if request.method=='POST': 3 user=request.POST.get('user') 4 pwd=request.POST.get('pwd ') 5 print(user,pwd) 6 return HttpResponse('ok') 7 return render(request,'login.html')
1:用form做用户验证的的时候,不需要做这些,直接将request.POST 传入form对象即可获取所有POST数据。
1 class Loginform(forms.Form): 2 user=forms.CharField(required=True)#需要继承forms.Form这个模块 3 pwd=forms.CharField(required=True)#require=True表示该字段不能为空。 4 5 def login(request): 6 if request.method=='POST': 7 form_obj=Loginform(request.POST)#获取浏览器提交所有数据 8 if form_obj.is_valid():#检测POST过来的数据是否符合我们form定义。返回值是bool 9 data=form_obj.clean()#通过对象的clean()方法来获取用户的提交的数据。 10 print(data) 11 return HttpResponse('ok') 12 else: 13 error=form_obj.errors##通过errors()方法 来获取我们的提交的数据错误提示。这个方法加了装饰器@property变成字段。 14 print(error) 15 return HttpResponse(error) 16 return render(request,'login.html')
前端页面代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <script src="/static/js/jquery-1.12.4.js"></script> 7 </head> 8 <body> 9 <div> <input name="user" type="text"></div> 10 <div><input name="pwd" type="password"></div> 11 <div><input type="button" value="提交" onclick="Submit(this)"></div> 12 <script> 13 function Submit(ths) { 14 var data_list={}; 15 $('input').each( 16 function (i) { 17 var name=$(this).attr('name'); 18 var val=$(this).val(); 19 data_list[name]=val; 20 } 21 ); 22 $.ajax({ 23 url:'/login/', 24 type:'POST', 25 data:data_list, 26 success:function (data) { 27 console.log(data) 28 }, 29 error:function (error) { 30 console.log(error) 31 } 32 }) 33 } 34 </script> 35 </body> 36 </html>
输出结果:
1 {'pwd': '111', 'user': '111'} 2 <ul class="errorlist"><li>pwd<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
输出打印error对象的类型:
1 <class 'django.forms.utils.ErrorDict'>
可以看出他是一个类对象,我们导入这个对象查看下他的方法:
1 error=form_obj.errors##通过errors()方法 来获取我们的提交的数据错误提示。这个方法加了装饰器@property变成字段。 2 from django.forms.utils import ErrorDict 3 print(type(error)) 4 return HttpResponse(error)
查看源码:
1 @html_safe 2 @python_2_unicode_compatible 3 class ErrorDict(dict): 4 """ 5 A collection of errors that knows how to display itself in various formats. 6 7 The dictionary keys are the field names, and the values are the errors. 8 """ 9 def as_data(self): 10 return {f: e.as_data() for f, e in self.items()} 11 12 def as_json(self, escape_html=False): 13 return json.dumps({f: e.get_json_data(escape_html) for f, e in self.items()}) 14 15 def as_ul(self): 16 if not self: 17 return '' 18 return format_html( 19 '<ul class="errorlist">{}</ul>', 20 format_html_join('', '<li>{}{}</li>', ((k, force_text(v)) for k, v in self.items())) 21 ) 22 23 def as_text(self): 24 output = [] 25 for field, errors in self.items(): 26 output.append('* %s' % field) 27 output.append('\n'.join(' * %s' % e for e in errors)) 28 return '\n'.join(output) 29 30 def __str__(self): 31 return self.as_ul()
我们可以看到当我们print的时候输出的as_ul。错误信息输出有三种数据格式:
1、ul格式,就是之前我们输出的样式:
1 <ul class="errorlist"><li>pwd<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
2:json格式。as_josn.
3:as_text.文本格式。 我们根据我们的需求来获得我们想要的格式。
下面用as_josn来获取我们的错误输出。
1 def login(request): 2 if request.method=='POST': 3 form_obj=Loginform(request.POST)#获取浏览器提交所有数据 4 if form_obj.is_valid():#检测POST过来的数据是否符合我们form定义。返回值是bool 5 data=form_obj.clean()#通过对象的clean()方法来获取用户的提交的数据。 6 print(data) 7 return HttpResponse('ok') 8 else: 9 error=form_obj.errors##通过errors()方法 来获取我们的提交的数据错误提示。这个方法加了装饰器@property变成字段。 10 print(type(error)) 11 error_msg=error.as_json()#获取错误信息json格式。 12 print(type(error_msg)) 13 return HttpResponse(error_msg) 14 return render(request,'login.html')
1 {"pwd": [{"message": "This field is required.", "code": "required"}], "user": [{"message": "This field is required.", "code": "required"}]}
因为传输的过程是字符串传输,可以在ajax出添加datatype:json来获取json串不需要进行转换。
我们自定义字典,方便我们取值。如上结果我们真正关心的是:“pwd”:[{"message": "This field is required."}]对于code的信息我们不关心。所以定义字典data_dic={"status":false,message:{None}}.
code:
1 def login(request): 2 res={'status':False,'message':{}} 3 if request.method=='POST': 4 form_obj=Loginform(request.POST)#获取浏览器提交所有数据 5 if form_obj.is_valid():#检测POST过来的数据是否符合我们form定义。返回值是bool 6 data=form_obj.clean()#通过对象的clean()方法来获取用户的提交的数据。 7 print(data) 8 res['status']=True#符合form规则为true 9 return HttpResponse('ok') 10 else: 11 error=form_obj.errors##通过errors()方法 来获取我们的提交的数据错误提示。这个方法加了装饰器@property变成字段。 12 error_msg=error.as_json() 13 print(type(error_msg))#as_json转换过来的数据是字符串类型。 14 print(error_msg) 15 res['status']=False#不符合form规则我们定义status为Flase。 16 err_msg=json.loads(error_msg)#将字符串格式的json转换成字典格式,方便我们取值。 17 for key,val in err_msg.items(): 18 res['message'].setdefault(key,val[0]['message'])#循环取‘user’,‘pwd’对应的报错信息。 19 print(res) 20 return HttpResponse(json.dumps(res))#将json格式的转换成字符串返回给浏览器客户端。 21 return render(request,'login.html')
如上所示我们需要给字符串格式的json转成我们处理的数据类型,并取我们想要的值给浏览器返回。
需要注意的是:ajax中指定返回数据类型是dataType是大写的T!!!!!!
页面输出我们错误提示:
前端页面代码:
1 <body> 2 <div> <input name="user" type="text"></div> 3 <div><input name="pwd" type="password"></div> 4 <div><input type="button" value="提交" onclick="Submit(this)"></div> 5 <script> 6 function Submit(ths) { 7 var data_list={}; 8 $('.error').remove();#每次提交的时候,需要删除错误提示。 9 $('input').each( 10 function (i) { 11 var name=$(this).attr('name'); 12 var val=$(this).val(); 13 data_list[name]=val; 14 } 15 ); 16 $.ajax({ 17 url:'/login/', 18 type:'POST', 19 data:data_list, 20 dataType:'json', 21 success:function (data) { 22 if(data.status){ 23 void 0 24 }else { 25 $.each( 26 data.message,function(key,val){ 27 var tag=document.createElement('span'); 28 tag.innerText=val; 29 tag.className='error';###注意不要用id因为一个html文本中id是唯一,他只能删除第一个。 30 $('input[name='+key+']').after(tag) 31 32 33 } 34 ); 35 } 36 }, 37 error:function (error) { 38 } 39 }) 40 } 41 </script> 42 </body>
效果:
如上代码行:8 需要用class来删除。因为如果用id进行删除的时候会出现:只删除第一个。而且id是唯一不能多次命名,多次提交会出现如下错误:
2:错误信息重写:
一:常规定义错误提示。
错误提示是英文。我们需要把英文错误提示转换成英文提示。
可以在我们后台form类中定义中文如下:
1 class Loginform(forms.Form): 2 user=forms.CharField(required=True,error_messages={'require':"用户名不能为空。"})#需要继承forms.Form这个模块 3 pwd=forms.CharField(required=True)#require=True表示该字段不能为空。
因为现在的错误提示是require做的限制。(required=True)。通过变量error_messages来定义错误。字典中key和我们做限制的参数名字一致。
效果:
这是对之前的错误信息重写。
密码错误提示重写。
1 class Loginform(forms.Form): 2 user=forms.CharField(required=True,error_messages={'required':"用户名不能为空。"})#需要继承forms.Form这个模块 3 pwd=forms.CharField(required=True,min_length=6, 4 max_length=10, 5 error_messages={'required':'密码不为空!', 6 'min_length':'不能少于6位!', 7 'max_length':'长度不能超过10位!'})#require=True表示该字段不能为空。
效果:
二:对django自己做的错误提示重写:
我们添加一列数字,默认required=True。
1 class Loginform(forms.Form): 2 user=forms.CharField(required=True,error_messages={'required':"用户名不能为空。"})#需要继承forms.Form这个模块 3 pwd=forms.CharField(required=True,min_length=6, 4 max_length=10, 5 error_messages={'required':'密码不为空!', 6 'min_length':'不能少于6位!', 7 'max_length':'长度不能超过10位!'})#require=True表示该字段不能为空。 8 num=forms.IntegerField()
效果:
我们可以在我们自定义form类中重写这个错误提示。
1 class Loginform(forms.Form): 2 user=forms.CharField(required=True,error_messages={'required':"用户名不能为空。"})#需要继承forms.Form这个模块 3 pwd=forms.CharField(required=True,min_length=6, 4 max_length=10, 5 error_messages={'required':'密码不为空!', 6 'min_length':'不能少于6位!', 7 'max_length':'长度不能超过10位!'})#require=True表示该字段不能为空。 8 num=forms.IntegerField(error_messages={'invalid':'请输入数字!','required':'输入不能为空!'})
效果:
通过:print打印出我们的错误信息吗,到code选项找到那个参数做的限制。
对于第一个不能为空为;required做的限制。
通过后端打印输出error_messages可以看到是那个参数做的限制。
1 error_msg=error.as_json() 2 print(type(error_msg)) 3 print(error_msg)
还要其他的form验证比如说IP、mail等等!
三:form验证扩展:如上是form为我们提供了验证。如果form里没有给我们提供验证呢?我们可以自定义验证扩展。
比如手机验证:
1 def mobile_validate(value): 2 mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') 3 if not mobile_re.match(value): 4 raise ValidationError('手机号码格式错误')
我们可以在我们form类中定义相应的字段,需要导入模块:from django.core.exceptions import ValidationError用参数:validators=[]后面是列表。
code:
1 from django.core.exceptions import ValidationError 2 import json,re 3 # Create your views here. 4 def mobile_validate(value): 5 mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') 6 if not mobile_re.match(value): 7 raise ValidationError('手机号码格式错误') 8 class Loginform(forms.Form): 9 user=forms.CharField(required=True,error_messages={'required':"用户名不能为空。"})#需要继承forms.Form这个模块 10 pwd=forms.CharField(required=True,min_length=6, 11 max_length=10, 12 error_messages={'required':'密码不为空!', 13 'min_length':'不能少于6位!', 14 'max_length':'长度不能超过10位!'})#require=True表示该字段不能为空。 15 num=forms.IntegerField(error_messages={'invalid':'请输入数字!','required':'输入不能为空!'}) 16 phone=forms.CharField(validators=[mobile_validate])#注意的是函数名不是字符串。
我们可以定required的error_message来指定我们想要的内容。
code:
1 phone=forms.CharField(validators=[mobile_validate,],error_messages={'required':'输入不能为空!'})
B:当页面有很多数据需要提交和验证,我们不能写每个input标签,当需要修改的时候,我们不能修改每个input标签,于是有第2种方法,form提交数据。
页面代码:
1 <body > 2 <div> 3 <form action="/login/" method="post"> 4 <div>{{obj_form.user }}</div> 5 {% if obj_form.errors %} 6 <span>{{ obj_form.errors.user.0}}</span> 7 {% endif %} 8 <div>{{ obj_form.pwd }}</div> 9 {% if obj_form.errors %} 10 <span>{{ obj_form.errors.pwd.0}}</span> 11 {% endif %} 12 <div>{{ obj_form.num }}</div> 13 {% if obj_form.errors %} 14 <span>{{ obj_form.errors.num.0}}</span> 15 {% endif %} 16 <div>{{ obj_form.phone }}</div> 17 {% if obj_form.errors %} 18 <span>{{ obj_form.errors.phone.0}}</span> 19 {% endif %} 20 <div><input type="submit" value="提交" ></div> 21 </form> 22 </div>
后台代码:
1 def login(request): 2 if request.method=='POST': 3 obj_login=Loginform(request.POST) 4 if obj_login.is_valid(): 5 return redirect('/login/') 6 else: 7 error=obj_login.errors 8 print(error['user'][0]) 9 return render(request,'login.html',{'obj_form':obj_login}) 10 obj_form_get=Loginform() 11 return render(request,'login.html',{'obj_form':obj_form})
总结:
1、form提交验证和ajax提交验证,后台没什么变化,只是前者返回的页面,后者返回的是HttpResponse字符串。
2、form提交的时候,第一次是get请求的,所以第一次的obj_form_get对象内不包含我们提交数据。而POST请求的时候,obj_login对象他包含2部门内容:1:我们在前台表单填写的数据。2:包含我们验证的错误提示。
3、再去错误提示的时候,error默认的数据格式如下:
类似于列表,其中内容部分,是索引为0的字段。所以取值的时候如后台和前台代码那样取值:error['user'][0] 、obj_form.errors.num.0。在js中.点代替python中的[].
4、由于返回页面的时候,默认带有我们添加的<span>标签。所以需要去掉这个标签。
所以在前台页面加入if判断,来确定是否需要添加span标签。
1 {% if obj_form.errors %} 2 <span>{{ obj_form.errors.phone.0}}</span> 3 {% endif %}
C:指定生成特定的标签和属性。
form类中添加相应的属性。
1 test=forms.CharField(widget=forms.TextInput(attrs={'class':'test'}))
在前端显示相应的标签:
1 <div>{{ obj_form.test }}</div>
效果:
也可以生成自定义的属性。或者select标签等。
1 choice=( 2 (0,'大连'), 3 (0,'沈阳'), 4 ) 5 class Loginform(forms.Form): 6 user=forms.CharField(required=True,error_messages={'required':"用户名不能为空。"})#需要继承forms.Form这个模块 7 pwd=forms.CharField(required=True,min_length=6, 8 max_length=10, 9 error_messages={'required':'密码不为空!', 10 'min_length':'不能少于6位!', 11 'max_length':'长度不能超过10位!'})#require=True表示该字段不能为空。 12 num=forms.IntegerField(error_messages={'invalid':'请输入数字!','required':'输入不能为空!'}) 13 phone=forms.CharField(validators=[mobile_validate,],error_messages={'required':'输入不能为空!'}) 14 test=forms.CharField(widget=forms.Select(choices=choice))