zoukankan      html  css  js  c++  java
  • python---django中form组件(数据添加前使用自定义方法<django预留扩展点3个>进行验证,以及源码分析)

    form组件代码:

    from app02.models import Userfrom django.core.exceptions import ValidationError
    
    class AjaxForm(forms.Form):
        user = fields.CharField(
            required=True,
            min_length=3,
            max_length=7,
        )
        email = fields.EmailField(
            required=True,
        )
    
        #自定义方法 clean_字段名
        #必须返回值self.cleaned_data['user']
        #如果出错抛出raise ValidationError("error...")
      #会在基础验证成功后,使用自定义方法进行验证
      

    #扩展点1
      #验证用户名唯一 def clean_user(self): v
    = self.cleaned_data['user'] if User.objects.filter(username=v).count(): #重复了 raise ValidationError("用户名已存在") return v    #验证邮箱....未完成 def clean_email(self): return self.cleaned_data['email']
      
      
      #扩展点2

       def clean(self):
        #self.cleaned_data中含有所有成功验证数据
        value_dict = self.cleaned_data
        v1 = value_dict.get('user')
        v2 = value_dict.get("email")
        # if User.objects.filter(username=v1,email=v2).count():
          # #重复了
          # raise ValidationError("username and email is exists")
        if v1=="root123" and v2=="66666da@qq.com":
          raise ValidationError("username and email is exists")
        return self.cleaned_data

    如果在数据库中发现重复数据,抛出错误。

    views代码:

    def ajax(req):
        if req.method == "GET":
            obj = AjaxForm()
            return render(req,"ajax.html",{"obj":obj})
        else:
            ret={}
            obj = AjaxForm(req.POST)
            if obj.is_valid():  #在使用is_valid之后才会将数据进行验证
                ret['status']="OK"
                return HttpResponse(json.dumps(ret))
            else:
                print(obj.errors)
                ret['message']=obj.errors
                return HttpResponse(json.dumps(ret))

    在is_valid后开始验证代码,从此处进入

    源码查看:

    class BaseForm:
           def is_valid(self):
            """
            Returns True if the form has no errors. Otherwise, False. If errors are
            being ignored, returns False.
            """若是表单数据正确,则返回true
            return self.is_bound and not self.errors #是属性方法,进行字段验证
    self.is_bound = data is not None or files is not None #data传入表单数据不为空,所以is_bound=true

    考虑self.errors:

        @property
        def errors(self):
            "Returns an ErrorDict for the data provided for the form"#返回错误信息为表单数据
            if self._errors is None:  #初始self._errors是null    
                self.full_clean()   #进入该方法
            return self._errors

    追踪self.full_clean():

        def full_clean(self):
            """
            Cleans all of self.data and populates 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()

    开始验证字段:self._clean_fields()

        def _clean_fields(self):
         #循环字段,在form组件中设置的字段,该字段来自于DeclarativeFieldsMetaclass的__new__
    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
             #上面尝试进行正则验证,验证成功后,开始下面逻辑
             #会调用 clean_%s方法name是字段名,这就是我们的自定义方法,我们可以在已经满足正则验证后的数据
             #上再次进行自定义函数的验证,比如验证数据库数据的唯一性处理
    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)

     这里进行循环字段验证,一次验证一个字段,而且字段顺序按照form组件中定义字段的顺序。

    可以看上面使用方法clean_user

    但是对于联合唯一,差了点。

    继续进行流程分析:(前提是前面没有抛出错误)

    分析def full_clean(self):中的第二个方法:self._clean_form()

        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

    其中调用了self.clean()方法:

        def clean(self):
            """  #预留钩子函数,在此处可以进行验证,出错抛出ValidationError错误 但是信息字段名是__all__,不是某一个字段名,比如user
                #第一个方法是针对某一个,那个key就是对应字段名(单个字段错误验证),第二个方法是针对所有字段(整体错误验证),所有是__all__
         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

    其中由于第一个方法成功,所以self.cleaned_data中含有所有字段信息,此处可以对所有字段一起进行验证

    也是一个扩展点

    可以看上面方法clean使用:

    补充:

        def _clean_form(self):
            try:
                cleaned_data = self.clean()
            except ValidationError as e:
                self.add_error(None, e)
         #此处添加错误时是None为字段名,为什么在后面变为__all__
            else:
                if cleaned_data is not None:
                    self.cleaned_data = cleaned_data

    查看代码add_error

        def add_error(self, field, error):
                error = {field or NON_FIELD_ERRORS: error.error_list}

    查看字段名NON_FIELD_ERRORS :空的定义

    NON_FIELD_ERRORS = '__all__'
    公共错误信息放在__all__中(不是字段验证产生的错误信息放置位置)
    错误信息存放形式,以字典存放
    
    {
        __all__:[],
        字段名1:[],
        字段名2:[],
        字段名3:[],
        ....
    }

    分析第三个方法:self._post_clean()

        def _post_clean(self):
            """
            An internal hook for performing additional cleaning after form cleaning
            is complete. Used for model validation in model forms.
            """
            pass

    这个方法也是一个扩展点,但是没有像前两个方法一样做错误捕获,我们需要自己做,由于前两个基本内容可以完成,这个可以不需要

  • 相关阅读:
    ABP-AsyncLocal的使用
    ABP-多个DbContext实现事物更新
    ABP取其精华
    VS2019和net core 3.0(整理不全,但是孰能生巧)
    consul部署多台Docker集群
    Arcgis runtime sdk .net 二次开发
    C# 依赖注入 & MEF
    自动构建环境搭建
    C# 调用C/C++动态链接库,结构体中的char*类型
    C# 调用C++DLL 类型转换
  • 原文地址:https://www.cnblogs.com/ssyfj/p/8684354.html
Copyright © 2011-2022 走看看