zoukankan      html  css  js  c++  java
  • 🍖forms组件源码分析

    引入

    • 分析forms 组件如何进行校验

    • 为何将校验成功的数据放入 cleaned_data

    • 检验失败的数据放入 errors

    • 代码示例 :

    # 进行校验 is_valid()
    if form_data.is_valid():
        print('校验成功')
        # 获得校验成功的数据 cleaned_data
        print(form_data.cleaned_data)
    else:
        print('检验失败')
        # 获得检验失败的错误提示 errors
        print(form_data.errors)
        error = form_data.errors.get('__all__')
    

    一.forms组件的检验机制

    forms组件最核心的就是 is_valid( ) 这个校验机制, 也就是我们的突破口

    1.Ctrl + 点击 is_valid( ) 查看其内部源码

    def is_valid(self):
        """Return True if the form has no errors, or False otherwise."""
        # 如果表单没有错误则返回True,否则返回False
        return self.is_bound and not self.errors
    

    也就是说让表单验证成功就必须保证 self.is_boundTrue , self.errorsFalse

    2.点击 self.is_bound 查看其源码

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

    只要数据部位空或者文件不为空, self.is_bound 就为 True, 也就是只要你传值了就为 True

    3.再返回上一步点击 self.errors 查看源码

    @property  # 类装饰器,可以伪装成属性来使用(不需要加括号)
    def errors(self):
        """Return an ErrorDict for the data provided for the form."""
        # 为表单提供的数据返回ErrorDict。
        if self._errors is None:  # 如果_errors中为空则执行full_clean()
        	self.full_clean()
        return self._errors
    

    4.点击进入 self._error

    self._errors = None  # Stores the errors after clean() has been called.
    

    发现其默认就是 None, 也就是说:无论如何都会执行 self.full_clean()

    5.在进入到 full_clean() 中查看

    • 只看关键逻辑, 其他的忽略
    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and self.cleaned_data.
        """
        # 对所有的self.data、self._errors、self.cleaned_data进行校验
        
        # 这里赋值了一个错误字典用来存放错误字段
        self._errors = ErrorDict()
        if not self.is_bound:  # Stop further processing(停止处理其他规则)
            return
        # 这个字典用来存正确字段
        self.cleaned_data = {}
        ......
    
        self._clean_fields()  # 字段校验和局部钩子方法
        self._clean_form()    # 全局钩子
        self._post_clean()    # 内部为pass,但可以自己添加
    

    6.我们在来看看校验字段的方法 _clean_fields()

    def _clean_fields(self):
        # 循环从fields中取出name和field
        # fields实质上是一个字典,forms组件的实例化就会自动创建一个fields
        # 该字典类内部类似 : {'name':'name字段对象','pwd':'pwd字段对象'}
        for name, field in self.fields.items():
    
            ......
            
            try:
                # 判断是不是文件,是文件则走下面的逻辑
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    # 不是文件则进行这一步,使用规则对象对传入的value进行校验(clean()才是真正的进行校验)
                    value = field.clean(value)
                # 然后将值加到字典中去,不成功则最下面抛出异常,将字段和错误信息加入到错误字典中
                self.cleaned_data[name] = value
                # 判断是否有'clean_%s'格式的一个变量名,也就是我们写的局部钩子函数(解释了为什么钩子函数名要那样写)
                if hasattr(self, 'clean_%s' % name):  
                    # 有的话就拿出来执行该钩子函数,得到返回数据
                    value = getattr(self, 'clean_%s' % name)()
                    # 将返回的数据添加到字典中,如果出错则也是走下面的except
                    self.cleaned_data[name] = value
            except ValidationError as e:
                # 将错误字段名和错误信息加入到errors字典中
                # 我们使用raise ValidationError('')抛出异常,与下面这个方法的效果类似
                self.add_error(name, e)
    

    7.点击 add_error 查看关键信息

    def add_error(self, field, error):
    	......
    	    ......
    	    
            if field in self.cleaned_data:  # 如果field字段对象在cleaned_data
                del self.cleaned_data[field]  # 那么就将其从中删除
    

    8.再来看看校验全局钩子的方法_clean_form()

    def _clean_form(self):
        try:
            cleaned_data = self.clean()  # 执行全局钩子并拿到返回值(父类中有clea()方法,如果你自己写了则执行你写的)
        except ValidationError as e:
            self.add_error(None, e)  # 出错则添加错误信息
        else:
            if cleaned_data is not None:  # 没出错则判断全局钩子的返回值是否为空
                self.cleaned_data = cleaned_data  # 不是空则原封不动的返回给elf.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__'.
        """
        # 由这个方法引发的任何验证错误将与名为'__all__'的字段关联
        # 父类中的全局钩子函数直接返回了cleaned_data
        return self.cleaned_data
    

    这就是为什么使用全局钩子的时候需要返回一个完整的cleaned_data, 否则将是一个不完整的数据

    三.总结源码分析步骤

    • is_vaild( ) 作为突破口, 查看 self.is_boundnot self.errors
    • 重点分析 self.errors 是否有值, 有值就是 False
    • 接着分析 errors 里面, 判断 _errors 是否都为空, 如果为空返回 full_clean(),否则返回_errors
    • 查看full_clean(),里面存放_errorscleaned_data这两个字典, 一个存错误字段, 一个存储正确字段
    • full_clean()里面还存放着forms组件的核心,比如self._clean_fields(): 用来校验字段
    • 查看_clean_fields()方法发现内部循环每个字段及字段对象, 而对其真正进行校验的是field.clean(value)方法
    • 并且可以知道分别将错误和正确的字段添加到_errorscleaned_data这两个字典中
    • 后面还设置了局部钩子函数的查找,如果找到则执行该钩子函数, 错误信息也会放入_errors
    • 字段和局部钩子校验完毕, 最后校验全局钩子
  • 相关阅读:
    setTimeOut与循环闭包问题
    ES6----class用法
    JS------对象的继承方式
    JavaScript对象 -构建
    nodejs异步---Async
    mongdb位置索引
    mongodb 索引3
    mongod 索引2
    mongodb 索引1
    3 C++数据类型
  • 原文地址:https://www.cnblogs.com/songhaixing/p/14589352.html
Copyright © 2011-2022 走看看