• django—Form组件校验方法(is_valid)执行流程


      1、从is_valid方法入手

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

        如果is_valid要返回True的话,那么self.is_bound要为True并且self.errors要为Flase(字面意思上看errors为False的话,表示没有错误信息)

      2、is_bound

        以下代码出现在BaseForm类中,也就是Form的父类(Form继承了父类的初始化方法)

        def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
         initial=None, error_class=ErrorList, label_suffix=None,
        empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None):
        self.is_bound = data is not None or files is not None

        is_bound为True的条件是data不为None,或者files不为None

        也就是说在初始化一个Form对象时,只要传入了data或者files,那么is_bound的属性就一定为True

        在调用is_valid方法校验数据之前,肯定需要将POST提交的数据用来初始化一个Form对象,然后调用该方法对数据进行校验

        所以,只要是用来校验POST提交的数据时,is_bound肯定为True    

          form_obj = RegForm(request.POST)  # 包含用户提交的数据的From对象
          if form_obj.is_valid(): # 对数据进行校验
          return HttpResponse('ok')

      3、errors

        @property
        def errors(self):
        """Return an ErrorDict for the data provided for the form."""
        if self._errors is None:
        self.full_clean()
        return self._errors

        errors实际上是一个方法,只是使用property装饰器装饰成了属性,可以通过对象.属性名的方式调用 

        当self._errors为None时会执行,full_clean方法。然后最终返回_errors。

        _errors同样定义在BaseForm的初始化方法内,初始化即为None,但是最终返回却将它作为结果返回。

        那么,说明full_clean内部肯定对_errors做了修改

        直接看full_clean方法内部做了些什么

      4、full_clean() 

        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 = {} # 创建了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()

        该方法首先赋值了一个错误字典给_errors(前面提到,初始化时为None),也就是说再经过一系列校验后,如果最后该字典为空,那么说明没有错误,即校验通过。

        接着往下看。

        如果is_bound为False直接终止方法执行,也就是该Form对象内没有数据,不需要校验。

        然后创建了一个字典cleaned_data,用于存放后续校验无误的数据。

        从这就可以知道,一个form对象只有在使用了is_valid()方法后,内部才会生成cleaned_data字典。使用is_valid()之前没有该变量。

        然后接着看后续调用校验方法的过程。

      5、_clean_fields() 

        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
        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) # 将捕获到的异常添加到错误字典中

        该方法内部,直接对fields进行迭代取值处理。

        fields内部存放的就是表单提交的数据,以字典形式存放,name对应的就是Form类中定义的属性名,field就是一个字段对象。

        如果当前迭代到的字段对象对应的input被禁用的话(disableed为True),那么就以该input的初始值作为该字段的值(initial设置的值。如果没有设置,得到的则是空字符串)。

        反之,该字段的值就是用户输入的值。

        

        接着,判断当前迭代到的字段对象类型,如果是文件类型,则使用文件相关的校验方式

        其他类型,都使用else代码块里的校验方式

        def clean(self, value):
        """
        Validate the given value and return its "cleaned" value as an
        appropriate Python object. Raise ValidationError for any errors.
        """
        value = self.to_python(value)
        self.validate(value)
        self.run_validators(value)
        return value

        validate(value)方法,即校验当前字段的值是否为空,如果为空则抛出required的提示信息(字段为必填项时会进行该校验)

        run_validators方法,执行的是当前字段默认的校验器(如果是Email字段,则它的默认校验器就会校验字段的值是否符合正确的邮箱格式)以及自定义的校验器(即设置字段的参数validators时,传入的自定义的校验函数对象)

        如果校验过程中出现异常,会层层抛出,每一层都在捕获异常,最终会将捕获到的异常添加到错误字段中。

        except ValidationError as e:
        self.add_error(name, e)

        然后最终返回原值。如果上述校验无误,则将该值添加到cleaned_data字典内。

        接着,判断有没有该字段的局部钩子函数,对该字段进行校验。

        有的话,就调用这个局部钩子就要该字段的值,校验无误就将该值重新赋值一遍给cleaned_data。出现异常就会抛出,执行异常处理函数,将错误信息添加到错误字典,并将该字段的值从cleaned_data字典内删除。

        从这也就能知道,局部钩子函数的返回值必须是对应字段的值原样返回,否则即使校验通过,在赋值时也会出错,导致最终得到的值不对。

       总结:

        clean方法主要功能就是执行字段内置校验方法、字段内置校验器以及自定义校验器

        _clean_fields方法只要功能就是执行clean、以及局部钩子函数,对数据进行校验。

      

      6、_clean_form()

        执行全局的钩子函数

        全局钩子函数 ,返回值必须是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

        因为,自定义的全局钩子函数,实际上就是重写了上述方法,最终返回值必须和上述方法用于,即校验无误的数据字典

      总结:

        整个is_valid()的主要的流程为:

          1、执行full_clean方法

            先定义一个错误字典用户后续判断校验是否正确,

            还有一个cleaned_data存放后续校验无误的数据

          2、执行_clean_fields方法

            该方法对数据进行除了全局钩子函数以外的校验

            先执行字段内置校验方法、字段内置校验器以及自定义校验器对数据进行校验

            然后执行局部钩子函数对数据进行校验

          3、执行_clean_form方法

            该方法对数据进行全局钩子函数的校验

        所有校验执行的先后顺序:

          字段内置的校验方法——>字段内置校验器——>字段自定义的校验器——>局部钩子函数——>全局钩子函数

      

        

  • 相关阅读:
    Mysql主从同步延迟问题及解决方案
    elasticsearch 查询过程
    RPC(Remote Procedure Call):远程过程调用
    windows
    设计模式
    Linux Safe
    AS
    开机启动
    springboot打包部署
    【Linux】Linux 常用命令汇总
  • 原文地址:https://www.cnblogs.com/yamx/p/13340479.html
Copyright © 2020-2023  润新知