Python入门自学进阶-Web框架——14Django的Form验证
Posted kaoa000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——14Django的Form验证相关的知识,希望对你有一定的参考价值。
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>
<script src="/static/jquery-3.6.0.js"></script>
<script>
$(function ()
$("#ajax_submit").click(function ()
$.ajax(
url: "logintest.html",
data: username:'root',email:'ww123@qq.com',pwd:'123123',csrfmiddlewaretoken: ' csrf_token ',
type:'POST',
success:function (arg)
console.log(arg);
)
)
)
</script>
</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: <ul class="errorlist"><li>username<ul class="errorlist"><li>用户名不能为空</li></ul></li><li>email<ul class="errorlist"><li>邮箱不能为空</li></ul></li></ul>
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),打印出类型:<class 'django.forms.utils.ErrorDict'>,是一个错误字典,看一下这个类的源代码:
@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是一个<class 'django.forms.utils.ErrorDict'>,使用obj.errors['email']访问一下,即
print(obj.errors['email'])结果为:<ul class="errorlist"><li>邮箱不能为空</li></ul>,这是不是一个字符串呢?打印类型print(type(obj.errors['email'])),结果为:<class 'django.forms.utils.ErrorList'>,是一个列表,可以通过下标获取: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="请输入用户名"><span> oo.errors.username.0 </span></p>
<p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"><span> oo.errors.email.0 </span></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'])
结果为:
<input type="text" name="username" minlength="4" required id="id_username">
<input type="email" name="email" required id="id_email">
对于不传参数的对象,obj['username']是生成一个input标签。
传递一个参数:obj=LoginForm('username':'qwert','email':'qw@123),则结果为
<input type="text" name="username" value="qwert" minlength="4" required id="id_username">
<input type="email" name="email" value="dd@123" required id="id_email">
在前端,可以使用这种方式自动生成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 <span> oo.errors.username.0 </span></p>
<p>用户邮箱: oo.email <span> oo.errors.email.0 </span></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>
<script src="/static/jquery-3.6.0.js"></script>
<script>
$(function ()
$("#ajax_submit").click(function ()
$.ajax(
url: "logintest.html",
data: username:'root',email:'ww123@qq.com',pwd:'123123',csrfmiddlewaretoken: ' csrf_token ',
type:'POST',
success:function (arg)
console.log(arg);
)
)
)
</script>
</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>
<style>
.error-msg
color: red;
font-size: 16px;
</style>
</head>
<body>
<form action="loginajax.html" method="post">
<p>用户名称:<input type="text" name="username" placeholder="请输入用户名"><span></span></p>
<p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"></p><span></span></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>
<script src="/static/jquery-3.6.0.js"></script>
<script>
$(function ()
$("#ajax_submit").click(function ()
$.ajax(
url: "loginajax.html",
data: $('form').serialize(),
type:'POST',
success:function (arg)
$('.error-msg').remove(); // 将具有class=error-msg的所有标签删除
var v1 = JSON.parse(arg);
//v1是一个json对象,内容:status: false, error: '"email": ["message": "\\\\u90ae\\\\u7bb1\\\\u540d\\\\u683c\\\\u…0d\\\\u7b26\\\\u5408\\\\u8981\\\\u6c42", "code": "invalid"]', data: null
//注意:其error的值为字符串
if(!v1.status)
var error_obj = JSON.parse(v1.error); //这里要对v1.error再次进行解析,因为其是字符串,否则下面的each出错
$.each(error_obj,function (k,v)
var tag = document.createElement('span');
tag.className = 'error-msg';
tag.innerHTML = v[0].message;
$("input[name='" + k +"']").after(tag);
)
else
location.href = 'loginajax.html';
)
)
)
</script>
</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>
<style>
.error-msg
color: red;
font-size: 16px;
</style>
</head>
<body>
<form action="loginajax.html" method="post">
<p>用户名称:<input type="text" name="username" placeholder="请输入用户名"><span></span></p>
<p>用户邮箱:<input type="text" name="email" placeholder="请输入用户邮箱"></p><span></span></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>
<script src="/static/jquery-3.6.0.js"></script>
<script>
$(function ()
$("#ajax_submit").click(function ()
$.ajax(
url: "loginajax.html",
data: $('form').serialize(),
type:'POST',
success:function (arg)
$('.error-msg').remove(); // 将具有class=error-msg的所有标签删除
var v1 = JSON.parse(arg);
//v1是一个json对象,内容:status: false, error: '"email": ["message": "\\\\u90ae\\\\u7bb1\\\\u540d\\\\u683c\\\\u…0d\\\\u7b26\\\\u5408\\\\u8981\\\\u6c42", "code": "invalid"]', data: null
//注意:其error的值为字符串
if(!v1.status)
// var error_obj = JSON.parse(v1.error); //这里要对v1.error再次进行解析,因为其是字符串,否则下面的each出错
var error_obj = v1.error; //对于后端两次反序列化的返回结果,v1.error是一个json对象了,不需要再次进行JSON.parse解析了,重点
alert(typeof error_obj);
$.each(error_obj,function (k,v)
// k:username或email
// v:[,,,]
var tag = document.createElement('span');
tag.className = 'error-msg';
tag.innerHTML = v[0].message;
$("input[name='" + k +"']").after(tag);
)
else
location.href = 'loginajax.html';
)
)
)
</script>
</body>
</html>
以上是Form验证的基本用法,日常开发中需要的其他一些问题:
- 除了字符验证和邮件验证,还有哪些验证,可不可以自己定制规则
- 除了生成input标签,是否可以生成其他标签
- 显示默认值
以上是关于Python入门自学进阶-Web框架——14Django的Form验证的主要内容,如果未能解决你的问题,请参考以下文章
Python入门自学进阶-Web框架——18FormModelForm
Python入门自学进阶-Web框架——20Django其他相关知识2
Python入门自学进阶-Web框架——3Django的URL配置