zoukankan      html  css  js  c++  java
  • Form的is_valid校验规则及验证顺序

    一、验证顺序
    • 查看form下的源码了解顺序
      BaseForm为基类,中间包含了is_valid校验方法
    @html_safe
    class BaseForm:
    .........
            self.is_bound = data is not None or files is not None
    .......
    
        @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
    
        def is_valid(self):  #--------------开始校验
            """Return True if the form has no errors, or False otherwise."""
            return self.is_bound and not self.errors   # ---------is_bound 中是数据和字段不能为空,否则就不校验,没问题后调用self.errors开始校验          
    .......    
    • is_valid 的校验顺序

    1. obj = MyForm(request.POST) 创建将要校验的实例

    2. obj.is_valid() 开始校验

    3. is_valid()校验 is_bound 查看我们创建的MyForm是否空字段,和实例中是否传入了(request.POST)数据

    4. is_valid() 调用self.errors 开始校验

    5. errors 中调用self.full_clean() 开始校验

        def full_clean(self):   #查看full_clean() 方法
            """
            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 = {}
            # 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() #这是个全局验证钩子,需要自己去子类里重写覆盖

    6.查看full_clean()方法中,最后三个函数 self._clean_fields()就开始校验了

        def _clean_fields(self):  #找到_clean_fields() 函数
            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) #调用field.clean()开始校验
                    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 #预留钩子用来自己做验证格式为 clean_字段名 这样的
                except ValidationError as e:
                    self.add_error(name, e)

    7.查看field.clean()它就是真的去校验了

        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

    8.验证完成(具体正则函数就不带着看了)

    1.2 总结顺序

    1. 首先is_valid()起手,看seld.errors中是否值,只要有值就是flase
    2. 接着分析errors.里面判断_errors是都为空,如果为空返回self.full_clean(),否则返回self._errors
    3. 现在就要看full_clean(),里面设置_errors和cleaned_data这两个字典,一个存错误字段,一个存储正确字段。
    4. 在full_clean最后有一句self._clean_fields(),表示校验字段
    5. 在_clean_fields函数中开始循环校验每个字段,真正校验字段的是field.clean(value),怎么校验的不管
    6. 在_clean_fields中可以看到,会将字段分别添加到_errors和cleaned_data这两个字典中
    7. 结尾部分还设置了钩子,找clean_XX形式的,有就执行。执行错误信息也会添加到_errors中
    8. 校验完成

    二、钩子验证
    • 以下为钩子源码:

    try:
    ...
        if hasattr(self, 'clean_%s' % name): 
            value = getattr(self, 'clean_%s' % name)()
            self.cleaned_data[name] = value #预留钩子用来自己做验证格式为 clean_字段名 这样的
    except ValidationError as e:
          self.add_error(name, e)

    可以看到钩子代码中使用
    try ... except ValidationError 错误并添加到errors中

    class MyForm(form.Form):
        ...
        # 钩子代码实例
        def clean_user(self):
            value = self.cleaned_data.get("user")
            # 从正确的字段字典中取值
            user_count = models.UserInfo.objects.filter(name=value).count()
            #查看数据库中这个用户是否存在
            if not value.isdigit():
                # 如果这个字符串全部都是由数组组成
                return value
            elif user_count:
                raise ValidationError("用户名已存在")
            else:
                # 注意这个报错信息已经确定了
                raise ValidationError("用户名不能全部是数字组成")
    
                # 在校验的循环中except ValidationError as e:,捕捉的就是这个异常
                # 所以能将错误信息添加到_errors中
        
        #全局钩子 _post_clean()
        def _post_clean():
            #自己在这儿全局验证,可以循环验证表单中所有的类容,
            #父类中默认 pass 占位,不操作
            pass

     

  • 相关阅读:
    Redis源码分析(二十一)--- anet网络通信的封装
    leetcode 总结part1
    leetcode String to Integer (atoi)
    leetcode 165. Compare Version Numbers
    leetcode 189. Rotate Array
    leetcode 168. Excel Sheet Column Title
    leetcode 155. Min Stack
    leetcode 228. Summary Ranges
    leetcode 204. Count Primes
    leetcode 6. ZigZag Conversion
  • 原文地址:https://www.cnblogs.com/ellisonzhang/p/10709970.html
Copyright © 2011-2022 走看看