python测试开发django-106.form表单中局部钩子(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属性中
注册示例
注册表单RegisterForm
from django import forms from django.core.exceptions import ValidationError # 作者-上海悠悠 QQ交流群:717225969 # blog地址 https://www.cnblogs.com/yoyoketang/ class RegisterForm(forms.Form): """注册表单""" username = forms.CharField(label="用户名", required=True, min_length=3, max_length=20, error_messages={'required': '不能为空', }) password = forms.CharField(max_length=16, min_length=6, required=True, label="密码", widget=forms.PasswordInput, error_messages={ 'required': '密码不能为空', 'min_length': '密码不能少于6位字符', 'max_length': '密码不能大于16位字符', }) email = forms.EmailField(required=False, error_messages={'invalid': '邮箱参数不合法'})
注册视图
from django.shortcuts import render from django.contrib.auth.models import User # 作者-上海悠悠 QQ交流群:717225969 # blog地址 https://www.cnblogs.com/yoyoketang/ def registerView(request): """注册视图""" if request.method == "GET": form_obj = RegisterForm() return render(request, "register_form.html", locals()) if request.method == "POST": form_obj = RegisterForm(request.POST) if form_obj.is_valid(): username = form_obj.cleaned_data.get("username") password = form_obj.cleaned_data.get("password") email = form_obj.cleaned_data.get("email") try: user = User.objects.create_user(username=username, password=password, email=email) user.save() error_msg = "注册成功!" except Exception as msg: error_msg = "注册账号异常" print("注册账号异常:", msg) return render(request, "register_form.html", locals())
注册模板
<form action="" method="POST" id="login-form" style="text-align:center;"> {% csrf_token %} {% for field in form_obj %} <p> {{ field.label_tag }} {{ field }} {{ field.errors }} </p> {% endfor %} <p> {{ error_msg }} </p> <p> <input type="submit" value="立即注册" > </p> </form>
当注册一个已存在的账号test,会报注册账号异常:(1062, "Duplicate entry 'test' for key 'username'")
因为数据库已经存在账号test, 写入数据库时会报这个错:Duplicate entry
局部钩子
在Form类里面定义局部钩子,校验数据库,格式 clean_校验字段(self):
from django import forms from django.core.exceptions import ValidationError # 作者-上海悠悠 QQ交流群:717225969 # blog地址 https://www.cnblogs.com/yoyoketang/ class RegisterForm(forms.Form): """注册表单""" username = forms.CharField(label="用户名", required=True, min_length=3, max_length=20, error_messages={'required': '不能为空', }) password = forms.CharField(max_length=16, min_length=6, required=True, label="密码", widget=forms.PasswordInput, error_messages={ 'required': '密码不能为空', 'min_length': '密码不能少于6位字符', 'max_length': '密码不能大于16位字符', }) email = forms.EmailField(required=False, error_messages={'invalid': '邮箱参数不合法'}) # 局部钩子 def clean_username(self): """判断数据库是否已存在""" val = self.cleaned_data.get('username') # 获取username字段值 user = User.objects.filter(username=val) # 在数据库中判断是否存在用户名 if not user: return val # 如果通过校验,那么把值直接原封不动返回即可 else: raise ValidationError('用户已经注册')
重复注册的时候,就会提示用户已被注册

全局钩子
在forms.py里面的BaseForm类可以看到clean方法,此方法是在每个Field字段校验之后触发,校验失败错误信息储存到 errors {'all':[e,]}。
校验成功返回self.cleaned_data
def clean(self): """ Hook for doing any extra form-wide cleaning after Field.clean() has been called on every field. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. """ return self.cleaned_data
于是可以重写clean()方法,校验输入的2次密码是不是一致
# 作者-上海悠悠 QQ交流群:717225969 # blog地址 https://www.cnblogs.com/yoyoketang/ class RegisterForm(forms.Form): """注册表单""" ...... # 全局钩子 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
在视图函数中可以拿到全局钩子错误信息:formobj.errors.get('_all')[0]
# 作者-上海悠悠 QQ交流群:717225969 # blog地址 https://www.cnblogs.com/yoyoketang/ def registerView(request): """注册视图""" if request.method == "GET": form_obj = RegisterForm() return render(request, "register_form.html", locals()) if request.method == "POST": form_obj = RegisterForm(request.POST) if form_obj.is_valid(): username = form_obj.cleaned_data.get("username") password = form_obj.cleaned_data.get("password") email = form_obj.cleaned_data.get("email") try: user = User.objects.create_user(username=username, password=password, email=email) user.save() error_msg = "注册成功!" except Exception as msg: error_msg = "注册账号异常" print("注册账号异常:", msg) else: # 全局钩子自定义错误提示获取 print(form_obj.errors.get('__all__')[0]) error_msg = form_obj.errors.get('__all__')[0] return render(request, "register_form.html", locals())
两次密码输入不一样后,在页面上的效果

