Python入门自学进阶-Web框架——14、Django的Form验证

打印 上一主题 下一主题

主题 891|帖子 891|积分 2673

Django的Form验证
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6. </head>
  7. <body>
  8.     <form action="logintest.html" method="post">
  9.         <p>用户名称:<input type="text" name="username" placeholder="请输入用户名"></p>
  10.         <p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"></p>
  11.         <p>用户邮箱:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
  12.         {% csrf_token %}
  13.         <input type="submit" value="Form提交">
  14.         <input id="ajax_submit" type="button" value="Ajax提交">
  15.     </form>
  16.    
  17.    
  18. </body>
  19. </html>
复制代码
  1. def logintest(req):
  2.     if req.method == "GET":
  3.         return render(req,'logintest.html')
  4.     elif req.method == "POST":
  5.         u = req.POST.get("username")
  6.         e = req.POST.get("email")
  7.         p = req.POST.get("pwd")
  8.         print("对获取的数据进行处理:1,校验;2,数据库操作",u,e,p)
  9.         return render(req,'logintest.html',{})
复制代码

存在的几个问题:
1、对用户提交的数据进行验证,要求提示信息准确,即哪个字段的输入不符合要求,在哪个字段进行提示错误信息;
2、如果提交的数据项比较多,后台req.POST.get()会大量出现;
3、如果要进行数据库操作,如使用filter()或create(),参数中要写大量如username=,email=,pwd=。。。等长的参数;
4、对于前端,如果数据项验证失败,即通过form提交了表单,此时前端的所有数据都清空了,而我们期望正确的输入框数据还在(当然,ajax提交不涉及这个问题,ajax提交后数据依然存在);
用户提交数据的验证
长度、类型、格式验证,重用性要高。
验证分为前端后后端
后端,使用一个模板:
- 邮箱格式
- 用户,名称长度>4
- 密码,长度>7
Django提供了Form验证类模板:
  1. # 定义模板
  2. from django import forms
  3. class LoginForm(forms.Form):
  4.     # 模板中的元素
  5.     username = forms.CharField(min_length=4)
  6.     email = forms.EmailField()
复制代码
使用验证类进行验证:
  1. def logintest(req):
  2.     if req.method == "GET":
  3.         return render(req,'logintest.html')
  4.     elif req.method == "POST":
  5.         obj = LoginForm(req.POST)
  6.         # 验证
  7.         status = obj.is_valid()
  8.         value_right = obj.clean()
  9.         print('value_right:',value_right)
  10.         value_error = obj.errors
  11.         print('value_error:',value_error)
  12.         print('status:',status)
  13.         if obj.is_valid():
  14.             value_right = obj.clean()
  15.             # create(**value_right)   数据库增加记录,参数直接使用**value_right
  16.         else:
  17.             value_error = obj.errors.as_json()
  18.             print('value_error_asjson:',value_error)
  19.         return render(req,'logintest.html',{})
复制代码
先定义一个模板,这个模板要继承自django的forms中的Form类,然后定义元素,注意,这里的变量名,如username、email不是随意取的,必须与前台form表单中各提交数据标签的name值一致,然后就是具有特定验证功能的CharField()、EmailField()等。在视图函数中使用这个模板
进行验证,先实例化一个模板对象,将req.POST作为参数,这样就会自动获取POST中的对应值进行验证。生成对象并不能验证,还需要调用is_valid()方法,这时才会进行验证,结果为True或False,验证无措,才会返回True,只要有一个字段验证错误,就是False。clean()方法是获取验证正确的字段的一个字典,errors则是错误字段及错误信息的一个无序列表字符串,使用as_json()转换为json字符串。

 因为email字段输入的不符合emailed格式,验证错误,value_error,即errors显示email的错误列表信息,转换为json字符串显示格式,is_valid()结果为false。clean()只是正确字段的字典。
这里有一个问题,就是如果模板中没有定义的字段,在clean()中不能获取,如这里的pwd字段,还需要使用POST.get()获取。
通过错误信息的json格式,可以看到错误的种类,即code的值,这里有invalid——格式不符合,min_length——最小长度不够,required——字段需要值,即字段为空了等。相应的message就是对应错误代码的说明信息,可以是汉字说明。 
  1. # 定义模板
  2. from django import forms
  3. class LoginForm(forms.Form):
  4.     # 模板中的元素
  5.     username = forms.CharField(min_length=4,error_messages={"min_length":"用户名长度不能小于4","required":"用户名不能为空"})
  6.     email = forms.EmailField(error_messages={"invalid":"邮箱名格式不符合要求","required":"邮箱不能为空"})
复制代码
运行打印结果:
value_right: {}
value_error:

  • username

    • 用户名不能为空

  • email

    • 邮箱不能为空


status: False
value_error_asjson: {"username": [{"message": "\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a", "code": "required"}], "email": [{"message": "\u90ae\u7bb1\u4e0d\u80fd\u4e3a\u7a7a", "code": "required"}]}
转换为json时,汉字转换为Unicode编码了。
 对于errors属性,通过打印结果看,好像是一个字符串,实际是什么呢?通过type(obj.errors),打印出类型:,是一个错误字典,看一下这个类的源代码:
  1. @html_safe
  2. class ErrorDict(dict):
  3.     """
  4.     A collection of errors that knows how to display itself in various formats.
  5.     The dictionary keys are the field names, and the values are the errors.
  6.     """
  7.     def as_data(self):
  8.         return {f: e.as_data() for f, e in self.items()}
  9.     def get_json_data(self, escape_html=False):
  10.         return {f: e.get_json_data(escape_html) for f, e in self.items()}
  11.     def as_json(self, escape_html=False):
  12.         return json.dumps(self.get_json_data(escape_html))
  13.     def as_ul(self):
  14.         if not self:
  15.             return ''
  16.         return format_html(
  17.             '<ul class="errorlist">{}</ul>',
  18.             format_html_join('', '<li>{}{}</li>', self.items())
  19.         )
  20.     def as_text(self):
  21.         output = []
  22.         for field, errors in self.items():
  23.             output.append('* %s' % field)
  24.             output.append('\n'.join('  * %s' % e for e in errors))
  25.         return '\n'.join(output)
  26.     def __str__(self):
  27.         return self.as_ul()
复制代码
继承自字典dict,我们看到了as_json(),返回的是json.dumps(),即转换为json格式字符串。而其__str__()是返回as_ul(),看as_ul(),其格式就是我们看到的打印的结果。
因为errors是一个,使用obj.errors['email']访问一下,即
print(obj.errors['email'])结果为:

  • 邮箱不能为空
,这是不是一个字符串呢?打印类型print(type(obj.errors['email'])),结果为:,是一个列表,可以通过下标获取:print('value_error:',value_error['email'][0]),结果:value_error: 邮箱不能为空。
也就是说,在生成obj对象时,相关的错误信息就存在对象中了,可以将此对象传递给前端:
  1. def logintest(req):
  2.     if req.method == "GET":
  3.         return render(req,'logintest.html')
  4.     elif req.method == "POST":
  5.         obj = LoginForm(req.POST)
  6.         # 验证
  7.         status = obj.is_valid()
  8.         value_right = obj.clean()
  9.         print('value_right:',value_right)
  10.         value_error = obj.errors
  11.         print('value_error:',value_error['email'][0])
  12.         print('status:',status)
  13.         if obj.is_valid():
  14.             value_right = obj.clean()
  15.             # create(**value_right)   数据库增加记录,参数直接使用**value_right
  16.         else:
  17.             value_error = obj.errors.as_json()
  18.             print('value_error_asjson:',value_error)
  19.         return render(req,'logintest.html',{'oo':obj})
复制代码
  1. <form action="logintest.html" method="post">
  2.         <p>用户名称:<input type="text" name="username" placeholder="请输入用户名">{{ oo.errors.username.0 }}</p>
  3.         <p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱">{{ oo.errors.email.0 }}</p>
  4.         <p>用户邮箱:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
复制代码
前端只需显示一个错误信息,所以只取索引0的值。对于第一个get请求,没有传递oo对象,对于django来说,没有的对象,返回的就是null,但对于其他语言,有可能出错。
现在的还有个问题是,form提交后,如果有字段出错,希望字段还保留输入的信息,要实现这个功能,就不能我们自己写input标签,需要Form来实现。
  1. obj = LoginForm()
  2. print(obj['username'])
  3. print(obj['email'])
复制代码
结果为:


对于不传参数的对象,obj['username']是生成一个input标签。
传递一个参数:obj=LoginForm({'username':'qwert','email':'qw@123}),则结果为


在前端,可以使用这种方式自动生成input标签:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6. </head>
  7. <body>
  8.     <form action="logintest.html" method="post">
  9.         <p>用户名称:{{ oo.username }}{{ oo.errors.username.0 }}</p>
  10.         <p>用户邮箱:{{ oo.email }}{{ oo.errors.email.0 }}</p>
  11.         <p>用户邮箱:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
  12.         {% csrf_token %}
  13.         <input type="submit" value="Form提交">
  14.         <input id="ajax_submit" type="button" value="Ajax提交">
  15.     </form>
  16.    
  17.    
  18. </body>
  19. </html>
复制代码
  1. def logintest(req):
  2.     if req.method == "GET":
  3.         obj = LoginForm()
  4.         return render(req,'logintest.html',{'oo':obj})
  5.     elif req.method == "POST":
  6.         obj = LoginForm(req.POST)
  7.         if obj.is_valid():
  8.             value_right = obj.clean()
  9.             # create(**value_right)   数据库增加记录,参数直接使用**value_right
  10.         else:
  11.             return render(req,'logintest.html',{'oo':obj})
复制代码
这样就能在输入错误后保留原数据。

 需要先运行is_valid()然后才能clean()
ajax实现提交验证,保留原输入值无需实现,ajax本身不刷新页面,输入值一直保持
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6.    
  7. </head>
  8. <body>
  9.     <form action="loginajax.html" method="post">
  10.         <p>用户名称:<input type="text" name="username" placeholder="请输入用户名"></p>
  11.         <p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"></p></p>
  12.         <p>用户密码:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
  13.         {% csrf_token %}
  14.         <input type="submit" value="Form提交">
  15.         <input id="ajax_submit" type="button" value="Ajax提交">
  16.     </form>
  17.    
  18.    
  19. </body>
  20. </html>
复制代码
前端:获取表单的数据,可以使用serialize()函数,这个获取到的数据可以直接作为ajax的data值使用。
  1. def loginajax(req):
  2.     if req.method == "GET":
  3.         return render(req,'loginajax.html')
  4.     elif req.method == "POST":
  5.         ret = {'status':True,'error':None,'data':None}
  6.         obj = LoginForm(req.POST)
  7.         if obj.is_valid():
  8.             print(obj.clean())
  9.             print(req.POST.get('pwd'))
  10.         else:
  11.             res_str = obj.errors.as_json()
  12.             ret['status'] = False
  13.             ret['error'] = res_str
  14.         return HttpResponse(json.dumps(ret))
复制代码
后端ajax提交需要直接HttpResponse返回一个json格式的字符串,使用了dumps()方法。
上面的实现方式,对于前端,在错误信息处理上,进行了两次JSON.parse(),有些繁琐。
对后端返回的错误信息进行处理:
  1. def loginajax(req):
  2.     if req.method == "GET":
  3.         return render(req,'loginajax.html')
  4.     elif req.method == "POST":
  5.         ret = {'status':True,'error':None,'data':None}
  6.         obj = LoginForm(req.POST)
  7.         if obj.is_valid():
  8.             print(obj.clean())
  9.             print(req.POST.get('pwd'))
  10.         else:
  11.             # res_str = obj.errors.as_json()
  12.             # ret['status'] = False
  13.             # ret['error'] = res_str
  14.             ret['status'] = False
  15.             ret['error'] = obj.errors.as_data()
  16.             # obj.errors.as_data()的值为:{'username': [ValidationError(['用户名不能为空'])], 'email': [ValidationError(['邮箱名格式不符合要求'])]}
  17.             # 其value中是如下类的实例:django.core.exceptions.ValidationError
  18.             # 将其进行一次反序列化,即将ValidationError(['用户名不能为空'])进行一次反序列化
  19.             # 最后将ret反序列化,这样一共进行了两次反序列化
  20.             # ValidationError(['用户名不能为空'])进行一次反序列化,因为其在ret中,所以可以在反序列化ret时,使用cls=参数,指定一个进行反序列化的类
  21.         return HttpResponse(json.dumps(ret,cls=JsonCustomEncode))
  22. from django.core.validators import ValidationError
  23. class JsonCustomEncode(json.JSONEncoder):
  24.     def default(self, field):
  25.         if isinstance(field,ValidationError):
  26.             return {'code':field.code,'message':field.message}
  27.         else:
  28.             return json.JSONEncoder.default(self,field)
复制代码
前端
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6.    
  7. </head>
  8. <body>
  9.     <form action="loginajax.html" method="post">
  10.         <p>用户名称:<input type="text" name="username" placeholder="请输入用户名"></p>
  11.         <p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"></p></p>
  12.         <p>用户密码:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
  13.         {% csrf_token %}
  14.         <input type="submit" value="Form提交">
  15.         <input id="ajax_submit" type="button" value="Ajax提交">
  16.     </form>
  17.    
  18.    
  19. </body>
  20. </html>
复制代码
以上是Form验证的基本用法,日常开发中需要的其他一些问题:
- 除了字符验证和邮件验证,还有哪些验证,可不可以自己定制规则
- 除了生成input标签,是否可以生成其他标签
- 显示默认值


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美食家大橙子

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表