python测试开发django-114.ModelForm中局部钩子(clean_)和全局钩子校验
前言
在实际开发中,不仅仅是对输入框字符的格式校验,比如注册功能,注册账号还得校验数据库是否已经有账号被注册过了。
有些场景不仅仅是对单个输入框的字符校验,比如修改密码的时候,会涉及2个输入框的数据格式校验,像这些复杂的场景校验需用到校验钩子来实现。
校验form表单数据合法性,is_valid()方法调用顺序:
1.字段规则校验,字符长度,是否必填等基本校验
2.validators校验(RegexValidator校验器或自定义校验函数)
3.局部钩子(类中定义的以clean_字段名命名的函数,校验正常必须返回该字段的值self.cleaned_data.get('name’))
4.全局钩子(类中定义的函数名clean,校验正常必须返回该对象的校验结果值return self.cleaned_data)
5.每一步通过校验单结果都以字典形式保存在类对象的cleaned_data属性中
ModelForm模型表单
局部钩子命名规则为clean字段名称,如:cleancity,clean_years。
super() 重写`__init`,可以批量更新class属性。
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
class SubmitPageForm(forms.ModelForm):
class Meta:
model = Submit
# fields = "__all__" #全部字段
fields = ["city", "years", "details"]
widgets = {
"city": widgets.TextInput(attrs={
"placeholder": "输入城市:北京/上海/深圳"
}),
"years": widgets.TextInput(attrs={
"placeholder": "输入年限"}),
"details": widgets.TextInput(attrs={
"placeholder": "输入详情"}),
}
labels = {
"city": "城 市",
"years": "年 限",
"details": "详 情",
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 批量更新class属性
for field in self.fields.values():
field.widget.attrs.update({'class': 'form-control'})
def clean_city(self):
"""局部钩子判断城市必须是北京/上海/深圳其中一个"""
city_val = self.cleaned_data.get('city', '')
if city_val in ["北京", "上海", "深圳"]:
return city_val
else:
raise forms.ValidationError('城市只能选:北京/上海/深圳')
定义视图
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
class SubmitView(View):
def get(self, request):
form_obj = SubmitPageForm
return render(request, "submit.html", locals())
def post(self, request):
form_obj = SubmitPageForm(request.POST)
if form_obj.is_valid():
# data = form_obj.cleaned_data()
form_obj.save()
msg = "保存成功"
return HttpResponseRedirect('/total')
else:
# 全局钩子自定义错误提示获取
# print(form_obj.errors.get('__all__'))
# error_msg = form_obj.errors.get('__all__')
return render(request, "submit.html", locals())
模板内容
模板内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>提交页面</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form role="form" action="" method="POST" id="detail-form" >
{% csrf_token %}
{% for field in form_obj %}
<div class="form-group">
{{ field.label_tag }}
{{ field }}
<div style="color: red"> {{ field.errors }} </div>
</div>
{% endfor %}
<p>
<input type="submit" value="提交" >
</p>
</form>
</div>
</body>
</html>
页面效果
输入不合法的内容,会显示field.errors内容
全局钩子
针对单个字段校验可以用局部钩子实现,如果我们要校验多个字段,比如校验注册的时候输入2次密码一致,可以用全局钩子实现。
定义全局钩子使用clean方法
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
class SubmitPageForm(forms.ModelForm):
class Meta:
model = Salary
fields = "__all__" #全部字段
# 省略中间代码。。。。。# 全局钩子
def clean(self):
"""在通过基础验证的干净数据中get获取字段"""
pwd1 = self.cleaned_data.get('password')
pwd2 = self.cleaned_data.get('password2')
if pwd1 and pwd2: # 这里判断2个字段都是校验通过
if pwd1 == pwd2:
# 数据没问题,那么原封不动返回即可
return self.cleaned_data
else:
# 错误信息储存到 errors {'__all__':[e,]}
raise ValidationError('两次密码输入不同')
else:
return self.cleaned_data
前端可以通过fomr_obj.errors.__all__
获取到内置校验器的全部错误信息