2.8.5Django --11.2 钩子源码解析

Django目录:https://blog.csdn.net/qq_41106844/article/details/105554082

我们在做钩子的源码解析之前,先分析一下forms组件使用的过程:
定义一个forms类

class UserForm(forms.Form):
    name=forms.CharField(max_length=5)
    pwd=forms.IntegerField()
    email=forms.EmailField()

向类中传递数据

form=UserForm(request.POST)

校验

form.is_valid()

保存校验正确的数据的字典

form.cleaned_data

保存校验错误的数据的字典

form.errors

分析一下我们先定义好模型,在传递数据,在校验,最后好的数据放一起,坏的数据放一起。
也就是说,最核心的是校验。
我们从校验的函数入手:
注意:我们是从form.is_valid进入的函数,也就是说,self是UserForm。

    def is_valid(self):
        """Return True if the form has no errors, or False otherwise."""
        return self.is_bound and not self.errors

分析:        
return  的返回值是一个布尔值,既当self.is_bound 与 not self.errors 都为True时,返回值为True,也就是校验成功,没有报错。

再进一步:

self.is_bound:
源码为
    self.is_bound = data is not None or files is not None

他判断了两个事情,
第一个事情是 data is not None
第二个事情是 files is not None。
也就是只要这两个有一个为True, self.is_bound就为True。
而这两件事情都是再说:传入的数据不是空的就行。

not self.errors:
self.errors 是用来保存报错的数据,如果没有报错的数据,也就是 self.errors 为 空,则 not self.errors 为True。
源码为
    @property
    def errors(self):
        """Return an ErrorDict for the data provided for the form."""
        if self._errors is None:   #这里的errors 为 None
            self.full_clean()
        return self._errors

errors中执行了一个方法:self.full_clean()


再进一步:

self.full_clean():
源码为
    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and self.cleaned_data.
        """
        self._errors = ErrorDict()   #拿到errors的初始变量。
        if not self.is_bound:  # Stop further processing.
            return
        self.cleaned_data = {}     #拿到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()

我们在full_clean里面拿到了 errors 和 cleaned_data 的初始变量。同时对我们传入的所有数据进行遍历校验,将正确的和错误的分别保存。


再进一步:


self._clean_fields():  这里是真正干活的方法
    def _clean_fields(self):
        for name, field in self.fields.items():  #self.fields 是保存表单数据的变量。 self.fields.items()就是取出我们的表单数据按照k-y  准备遍历。
        #name是字段字符串,field是规则对象(表单数据),例如传入用户名 alex,密码 123  这里的第一次循环就是  'name',name  第二次循环就是 'pwd',pwd
            # 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))
            # 不管残不残缺,最后获取的 value  就是我们 name代表的值 也就是  ‘alex’
            try:
                if isinstance(field, FileField):  #因为我们的是CharField类型,执行else之后的。
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    #这里是按照我们的规则对象校验我们的值
                    #我们定义的规则是长度不能超过5位, 显然我们的值符合规则。
                    #要是校验失败了,会抛出一个  ValidationError 异常,就会执行最后一句。
                    value = field.clean(value)
                #校验通过的话,将name和value 以k-y的形式添加到 cleaned_data中。
                self.cleaned_data[name] = value
                #正常校验到上面就结束了,下面是我们的钩子。
                #这里会去我们的类里面找  有没有  以clean_开头,以我们的name结尾的方法,这就是我们的自定义钩子方法。
                if hasattr(self, 'clean_%s' % name):
                    #有的话,就提出我们的自定义钩子,然后执行以下。
                    value = getattr(self, 'clean_%s' % name)()
                    #同上面的意思,校验成功的添加进来,失败了跳转最后一句。
                    self.cleaned_data[name] = value
            except ValidationError as e:
                #将报错的name 与 相对应的错误信息添加到 errors 中。
                self.add_error(name, e)

到这里为止,好像我们的源码结束了,但是仔细想一下:
假如我们正常校验过了,但是自定义钩子校验没过,这样的话cleaned_data和errors中都有我们的name信息,这显然是不符合常理的,接着我们来一下 self.add_error :

    def add_error(self, field, error):
      '''
      这里删除了一些与我们分析无关的东西
      '''
        for field, error_list in error.items():
            if field not in self.errors:
                if field != NON_FIELD_ERRORS and field not in self.fields:
                    raise ValueError(
                        "'%s' has no field named '%s'." % (self.__class__.__name__, field))
                if field == NON_FIELD_ERRORS:
                    self._errors[field] = self.error_class(error_class='nonfield')
                else:
                    self._errors[field] = self.error_class()
            self._errors[field].extend(error_list)
            #在这里执行了一句,如果我们的规则对象也在cleaned_data,那就删掉他。
            if field in self.cleaned_data:
                del self.cleaned_data[field]

我们再过一下全局钩子的源码

    def _clean_form(self):
        try:
            #看这一句的clean()方法
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
        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__'.
        """
        #clean()方法直接把 cleaned_data  返回了回去。
        return self.cleaned_data


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒 暄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值