• Django13-ModelForm中的is_valid及局部钩子、全局钩子源码解析


    1、查看is_valid方法,返回self.is_bound和非self.errors

    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

    2、查看self.is_bound方法,可以看到data或者files只要有一个不为空,即为真

    self.is_bound = data is not None or files is not None

    3、查看self.errors方法,可以看到判断self._errors如果为None,就执行full_clean()方法,(通过查看self._errors可以看到默认就是None,self._errors = None)

     @property #装饰器将方法变成属性
     def errors(self):
         "Returns an ErrorDict for the data provided for the form"
         if self._errors is None:
             self.full_clean()
         return self._errors

    4、查看self.full_clean方法

    执行了self._clean_fields、self._clean_form和self._post_clean三个方法,这3个方法都执行完成后,full_clean方法就执行完成了。

    def full_clean(self):
            """
            Cleans all of self.data and populates self._errors and
            self.cleaned_data.
            """
            self._errors = ErrorDict()    #存放错误信息的字典,ErrorDict继承了dict
            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()

    5、查看self._clean_fields方法

    def _clean_fields(self):
            for name, field in self.fields.items():    #将字典中的所有值都循环出来
                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:
                        #执行字段中的clean方法(内置校验器的)和自定义校验器的校验,如果有错误就执行下面的add_error方法
                        value = field.clean(value)
                    #如果内置校验器和自定义检验器都通过后就将该字段添加到cleaned_data字典中
                    self.cleaned_data[name] = value
                    如果没有错误,通过反向判断当前form里是否有定义的局部钩子,如果有就加括号执行这个局部钩子的方法,如果错误就执行下面的add_error方法,这里就是源码预留的局部钩子的定义方法,命名为clean_字段名称
                    if hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()
                        self.cleaned_data[name] = value  #局部钩子校验通过后将数据保存到cleaned_data字典中,没有通过校验就抛出异常
                except ValidationError as e:
                    self.add_error(name, e)

    6、查看self._clean_form方法

    内置校验器、自定义校验器、局部钩子都执行校验通过后,再执行self._clean_form方法中的self.clean方法

    def _clean_form(self):
            try:
                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

    7、查看self.clean方法

    这里就是源码预留的全局钩子的定义方法,如果定义了全局钩子并通过校验,就返回所有数据

    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

    8、都执行完成后,将错误信息保存在第4步中的self._errors = ErrorDict()这个错误字典中,如果没有错误信息,那么 self._errors = ErrorDict()这个错误字典就是空的。
    第3步中的self.full_clean()就执行完成了,并返回return self._errors字典
    第1步中的is_valid()就执行完了,如果没有错误信息,return self.is_bound and not self.errors就返回True,否则就返回False


    执行顺序:

    1. 内置校验器
    2. 自定义校验器
    3. 局部钩子
    4. 全局钩子
  • 相关阅读:
    Undo/Redo实现
    [CruiseControl]binary安装和启动
    perl读写文件和命令行解析
    C++跨平台IDE之CodeBlocks
    Swig实现多语言接口
    让人梦萦的西安小吃(转载)
    推荐:Visual Basic.NET Windows Forms 编程
    健康生活16个健康习惯
    推荐一个ASP的内容管理框架(ASP Nuke)
    《企业应用架构模式》读书笔记(1)
  • 原文地址:https://www.cnblogs.com/dxnui119/p/10823277.html
Copyright © 2020-2023  润新知