1 forms组件源码分析 1 为什么局部钩子要写成clean_字段名,为什么要抛异常2 入口在is_valid()3 校验流程-先校验字段自己的规则(最大,最小,是否必填,是不是合法)-校验局部钩子函数
1 forms组件源码分析
1 为什么局部钩子要写成clean_字段名,为什么要抛异常
2 入口在is_valid()
3 校验流程
-先校验字段自己的规则(最大,最小,是否必填,是不是合法)
-校验局部钩子函数
-全局钩子校验
4 流程
-is_valid() --> return self.is_bound and not self.errors
-self.errors: 方法包装成了数据属性
-一旦有值,self.errors就不进行校验(之前调用过了)
- self.full_clean(): 核心
if not self.is_bound: # Stop further processing. 如果data没有值直接返回
return
def full_clean(self):
"""
Clean all of self.data and populate self._errors and self.cleaned_data.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()
# 局部钩子执行位置
def _clean_fields(self):
for name, field in self.fields.items():
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value) # 字段自己的校验规则
self.cleaned_data[name] = value # 把校验后的数据放到cleaned_data
if hasattr(self, 'clean_%s' % name): # 判断有没有局部钩子
value = getattr(self, 'clean_%s' % name)() # 执行局部钩子
self.cleaned_data[name] = value # 校验通过 把数据替换一下
except ValidationError as e: # 如果校验不通过,会抛异常,会被捕获
self.add_error(name, e) # 捕获后执行 添加到错误信息 errors是个列表 错误可能有多个
# 全局钩子执行位置
def _clean_form(self):
try:
# 如果自己定义的form类中写了clean,他就会执行
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e) # key作为None就是__all__
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data