nd为 True,所以进入self.errors
方法,为什么没加括号呢,因为被property
装饰了,变成了属性。
self._errors
为None
,进入full_clean()
方法。
在这个方法里,首先定义了cleaned_data
为一个空字典,然后就是下面的三个方法:
- _clean_fields():见名思义,这个方法是用来校验字段的,就是继承自 Form 中类里面定义的字段。
光看源码不看例子很难理解,
class MyForm(forms.Form):
name = forms.CharField(min_length=3, max_length=8, label='用户名', required=True, error_messages={'min_length': '用户名长度至少为三位', 'max_length': '用户名长度最长为八位', })
pwd = forms.CharField(min_length=3, max_length=8, label='密码', required=True, error_messages={'min_length': '用户名长度至少为三位', 'max_length': '用户名长度最长为八位', })
email = forms.EmailField(label='邮箱', error_messages={'invalid': '邮箱格式不合法', 'required': '该字段必填'})
在self.fields
中放的是一个个键值对,比如在上面的MyForm
里面的字段name
和后面的CharField
类的一个对象的对应关系,那么在self.fields
里面就有三个对应关系,也就是有三个键值对。
往下走,字典的items()
方法,把MyForm
里面的字段值赋给了name
,把字段指向的类对象赋给了field
,之后进行判断,if field.disable:
,这个判断是判断类对象的某个属性值,可以知道在定义字段时,并没有定义这个值,那么这个值是否有默认值,进入forms.CharField
中查看,
可以看出该类中没有,那么去父类中查看也就是Field
:
找到了,默认为 False, 所以说if field.disables:
为 False, 就走到了下一步:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
这句看不懂,接着往下走:
首先,咱们定义的都不是FileField
字段,所以走value=field.clean(value)
:
因为是字段的方法,所以找forms.CharField
,在Field
类中找到:
这里面有点绕,我先把顺序写出来:
首先要知道value
到底是什么:
在上面说过调用了field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
方法,是字段的方法,
在CharField
中没找到widget
,去Field
中寻找,
可以看到widget=TextInput
,所以肯定是调用了Textinput
的value_from_datadict
方法,
没有,去Input
,
还是没有,去Widget
,
找到,返回了data.get(name)
,很显然这是字典的取值方法,但是data
是什么呢?
data
是个QueryDict
对象,找到name
是什么,就可以知道value
是什么了。
可以知道name
是通过self.add_prefix(name)
获取的,进入:
是个三元表达式,关键在于self.prefix
的值是多少。这是个对象属性,很显然去类中找:
所以这个三元表达式的值就是MyForm
中定义的字段名,所以通过data.get(name)
取得值就是上传的值,比如musibii
。
那么执行field.clean(value)
相当于把musibii
传进去执行,
在field.clean
方法中:
- to_python('musibii'):因为
CharField
中定义了to_python
方法,所以进入自己的方法;
musibii
不是None,空字符串、空字典、空元祖,所以进入forece_text('musibii')
方法:
别看这么多,其实就进行了一步判断:if issubclass(type('musibii'), six.text_type):
因为这个判断为True
,
所以直接把musibii
返回。然后判断if self.strip:
因为该参数默认为True
,所以value=value.strip()
,默认是通过空格进行分割的,走完后后进入self.valitate('musibii')
,
直接退出进入run_validators('musibii')
,
在这个方法里面,主要是进行了字段的一些限制校验,比如长度等,musibii
没有错误,返回musibii
,然后把该数据加到self.cleaned_data
中,表示这个数据是成功通过校验的。
这句代码是反射用法,判断self
是否具有clean_name
属性,(name 变量的值为 name),因为没有,所以第一个字段的校验结束,进行第二个字段的校验。
如果我们在MyForm
类中定义了clean_name
或者clean_pwd
或者clean_email
方法,如果在_clean_fields
方法中通过校验就会执行自定义的校验方法,这就是局部钩子。
class RegForm(forms.Form):
name = forms.CharField(max_length=8, min_length=3, label='用户名',
error_messages={'max_length': '太长了',
'min_length': '太短了',
'required': '该项不能为空'
},
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
pwd = forms.CharField(max_length=8, min_length=3, label='密码',
error_messages={'max_length': '太长了',
'min_length': '太短了',
'required': '该项不能为空'
},
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
re_pwd = forms.CharField(max_length=8, min_length=3, label='确认密码',
error_messages={'max_length': '太长了',
'min_length': '太短了',
'required': '该项不能为空'
},
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(label='邮箱', error_messages={'invalid': '格式不正