forms组件钩子
局部钩子
对字段校验有特殊的需求,此时可以使用局部钩子来增加校验内容 下面的例子中判断前端页面传过来的user是否是以yuan开头的字符串
示例代码 点击is_valid,引入ValidationError
from django.core.exceptions import ValidationError
class UserForm(forms.Form):
user = forms.CharField(
label="用户名", #自定义form表单显示到网页的名字
min_length=5,
error_messages={"required": "不能为空", "min_length": "最小长度不能小于5"}, #自定义错误提示信息
widget=widgets.TextInput(attrs={"class": "form-control"}) #自定义属性,添加一个Bootstrap样式
)
def clean_user(self): #clean_是固定写法
val = self.cleaned_data.get("user") #cleaned_data里面是经过form组件默认规则校验后的内容
if val.startswith("yuan"): #如果校验通过则返回原值
return val
else: #如果校验未通过则抛错
raise ValidationError("用户名必须以yuan开头")
解析源码 由于最终校验是使用is_valid方法,那么我们看看is_valid方法里面有什么
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not self.errors #只有self.errors为空是才返回True
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None: #第一次运行当然为None
self.full_clean() #去看看full_clean执行了什么
return self._errors
def full_clean(self):
...
self._clean_fields() #校验字段,这就是局部钩子关键所在,进去看看
self._clean_form()
_clean_fields中将校验字段及规则做循环
def _clean_fields(self):
for name, field in self.fields.items(): #self.fields就是需要校验的字段,{"user":user字段的规则对象}
...
try:
...
else:
value = field.clean(value) #判断是否符合校验规则,不符合直接抛错,符合则继续往下走
self.cleaned_data[name] = value #如果校验成功则存入cleaned_data字典
if hasattr(self, 'clean_%s' % name): #钩子来源于这里,通过反射查询当前类下有没有一个叫'clean_%s' % name的方法
value = getattr(self, 'clean_%s' % name)() #执行这个方法,返回值交给value
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e) #如果报错,则将字段名和错误信息组成键值对放入到errors字典中去
全局钩子
校验的规则涉及到多个字段值 比如:判断两次密码是否一致 全局错误在errors中的键值为"all" 通过form.errors["all"][0]来获取
示例代码 举例判断用户名和电话是否一致
def clean(self): #函数名是固定写法
user = self.cleaned_data.get("user")
tel = self.cleaned_data.get("tel")
if user == tel:
return self.cleaned_data #如果验证成功的固定写法
else:
raise ValidationError("用户名与电话不同")
源码解析
def full_clean(self):
...
self._clean_fields()
self._clean_form() #全局钩子
def _clean_form(self):
try:
cleaned_data = self.clean() #执行clean方法
except ValidationError as e: #如果报错同样可以捕获到
self.add_error(None, e) #做全局校验没有具体的字段,这个键值叫做'__all__'
else:
if cleaned_data is not None:
self.cleaned_data = 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也要返回self.cleaned_data
这个clean就是留给我们的接口,我们自己写一个clean就不需要走这里了 全局错误都在form.errors["all"][0]中