python---django中form組件(數據添加前使用自定義方法 進行驗證,以及源碼分析)

form組件代碼:html

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_dataajax

若是在數據庫中發現重複數據,拋出錯誤。數據庫

views代碼:django

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後開始驗證代碼,今後處進入json

源碼查看:app

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:ide

    @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()post

    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組件中定義字段的順序。ui

能夠看上面使用方法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

這個方法也是一個擴展點,可是沒有像前兩個方法同樣作錯誤捕獲,咱們須要本身作,因爲前兩個基本內容能夠完成,這個能夠不須要

相關文章
相關標籤/搜索