Django的Form验证
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="logintest.html" method="post">
- <p>用户名称:<input type="text" name="username" placeholder="请输入用户名"></p>
- <p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"></p>
- <p>用户邮箱:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
- {% csrf_token %}
- <input type="submit" value="Form提交">
- <input id="ajax_submit" type="button" value="Ajax提交">
- </form>
-
-
- </body>
- </html>
复制代码- def logintest(req):
- if req.method == "GET":
- return render(req,'logintest.html')
- elif req.method == "POST":
- u = req.POST.get("username")
- e = req.POST.get("email")
- p = req.POST.get("pwd")
- print("对获取的数据进行处理:1,校验;2,数据库操作",u,e,p)
- return render(req,'logintest.html',{})
复制代码
存在的几个问题:
1、对用户提交的数据进行验证,要求提示信息准确,即哪个字段的输入不符合要求,在哪个字段进行提示错误信息;
2、如果提交的数据项比较多,后台req.POST.get()会大量出现;
3、如果要进行数据库操作,如使用filter()或create(),参数中要写大量如username=,email=,pwd=。。。等长的参数;
4、对于前端,如果数据项验证失败,即通过form提交了表单,此时前端的所有数据都清空了,而我们期望正确的输入框数据还在(当然,ajax提交不涉及这个问题,ajax提交后数据依然存在);
用户提交数据的验证:
长度、类型、格式验证,重用性要高。
验证分为前端后后端
后端,使用一个模板:
- 邮箱格式
- 用户,名称长度>4
- 密码,长度>7
Django提供了Form验证类模板:
- # 定义模板
- from django import forms
- class LoginForm(forms.Form):
- # 模板中的元素
- username = forms.CharField(min_length=4)
- email = forms.EmailField()
复制代码 使用验证类进行验证:
- def logintest(req):
- if req.method == "GET":
- return render(req,'logintest.html')
- elif req.method == "POST":
- obj = LoginForm(req.POST)
- # 验证
- status = obj.is_valid()
- value_right = obj.clean()
- print('value_right:',value_right)
- value_error = obj.errors
- print('value_error:',value_error)
- print('status:',status)
- if obj.is_valid():
- value_right = obj.clean()
- # create(**value_right) 数据库增加记录,参数直接使用**value_right
- else:
- value_error = obj.errors.as_json()
- print('value_error_asjson:',value_error)
- 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就是对应错误代码的说明信息,可以是汉字说明。
- # 定义模板
- from django import forms
- class LoginForm(forms.Form):
- # 模板中的元素
- username = forms.CharField(min_length=4,error_messages={"min_length":"用户名长度不能小于4","required":"用户名不能为空"})
- email = forms.EmailField(error_messages={"invalid":"邮箱名格式不符合要求","required":"邮箱不能为空"})
复制代码 运行打印结果:
value_right: {}
value_error:
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),打印出类型:,是一个错误字典,看一下这个类的源代码:
- @html_safe
- class ErrorDict(dict):
- """
- A collection of errors that knows how to display itself in various formats.
- The dictionary keys are the field names, and the values are the errors.
- """
- def as_data(self):
- return {f: e.as_data() for f, e in self.items()}
- def get_json_data(self, escape_html=False):
- return {f: e.get_json_data(escape_html) for f, e in self.items()}
- def as_json(self, escape_html=False):
- return json.dumps(self.get_json_data(escape_html))
- def as_ul(self):
- if not self:
- return ''
- return format_html(
- '<ul class="errorlist">{}</ul>',
- format_html_join('', '<li>{}{}</li>', self.items())
- )
- def as_text(self):
- output = []
- for field, errors in self.items():
- output.append('* %s' % field)
- output.append('\n'.join(' * %s' % e for e in errors))
- return '\n'.join(output)
- def __str__(self):
- 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对象时,相关的错误信息就存在对象中了,可以将此对象传递给前端:
- def logintest(req):
- if req.method == "GET":
- return render(req,'logintest.html')
- elif req.method == "POST":
- obj = LoginForm(req.POST)
- # 验证
- status = obj.is_valid()
- value_right = obj.clean()
- print('value_right:',value_right)
- value_error = obj.errors
- print('value_error:',value_error['email'][0])
- print('status:',status)
- if obj.is_valid():
- value_right = obj.clean()
- # create(**value_right) 数据库增加记录,参数直接使用**value_right
- else:
- value_error = obj.errors.as_json()
- print('value_error_asjson:',value_error)
- return render(req,'logintest.html',{'oo':obj})
复制代码- <form action="logintest.html" method="post">
- <p>用户名称:<input type="text" name="username" placeholder="请输入用户名">{{ oo.errors.username.0 }}</p>
- <p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱">{{ oo.errors.email.0 }}</p>
- <p>用户邮箱:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
复制代码 前端只需显示一个错误信息,所以只取索引0的值。对于第一个get请求,没有传递oo对象,对于django来说,没有的对象,返回的就是null,但对于其他语言,有可能出错。
现在的还有个问题是,form提交后,如果有字段出错,希望字段还保留输入的信息,要实现这个功能,就不能我们自己写input标签,需要Form来实现。
- obj = LoginForm()
- print(obj['username'])
- print(obj['email'])
复制代码 结果为:
对于不传参数的对象,obj['username']是生成一个input标签。
传递一个参数:obj=LoginForm({'username':'qwert','email':'qw@123}),则结果为
在前端,可以使用这种方式自动生成input标签:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="logintest.html" method="post">
- <p>用户名称:{{ oo.username }}{{ oo.errors.username.0 }}</p>
- <p>用户邮箱:{{ oo.email }}{{ oo.errors.email.0 }}</p>
- <p>用户邮箱:<input type="password" name="pwd" placeholder="请输入用户密码"></p>
- {% csrf_token %}
- <input type="submit" value="Form提交">
- <input id="ajax_submit" type="button" value="Ajax提交">
- </form>
-
-
- </body>
- </html>
复制代码- def logintest(req):
- if req.method == "GET":
- obj = LoginForm()
- return render(req,'logintest.html',{'oo':obj})
- elif req.method == "POST":
- obj = LoginForm(req.POST)
- if obj.is_valid():
- value_right = obj.clean()
- # create(**value_right) 数据库增加记录,参数直接使用**value_right
- else:
- return render(req,'logintest.html',{'oo':obj})
复制代码 这样就能在输入错误后保留原数据。

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