django源碼閱讀 forms表單驗證部分

Django源碼閱讀之forms表單

在我們在寫web後端的時候,我們都要對前端傳入的數據進行驗證,判斷是否合法。在django框架中,我們就需要使用到forms這個東西了。不清楚forms的用法可以參考這篇博客django使用表單驗證前端傳入數據並儲存

所以forms表單在我們開發web項目的時候用的也是很多的一個點,接下來我們就來閱讀一下django中forms表單的源碼。

首先我們知道在django中使用表單,首先得寫一個類,然後繼承django.forms.Form,然後在這個類裏面編寫我們需要驗證的字段。例如下面這樣。

from django import forms

class TestForm(forms.Form):
    name = forms.CharField(max_length=20, min_length=10, error_messages={
        'required':'name不能爲空',
        'max_length':'name不能少於10個字符',
        'min_length':'name不能超過20個字符'
    })
    password = forms.CharField(max_length=16, min_length=6, error_messages={
        'required':'密碼不能爲空',
        'max_length':'密碼不能超過16個字符',
        'min_length':'密碼不能少於6個字符'
    })
    email = forms.EmailField(error_messages={
        'required':'郵箱不能爲空',
        'invalid':'請輸入格式正確的郵箱'
    })

當然,上面我只是隨便寫了三個需要驗證的字段。我們還可以寫更多,這裏就不演示了。

在我們定義好了這個Form類的時候,相當一定義好了我們的驗證器了,那麼我們在需要使用的位置導入這個Form類。然後開始使用。

例如,在視圖中.

from .forms import TestForm

def test(request):
    form = TestForm(request.POST)
    if form.is_valid():
        ...
    else:
        ...

首先我們先實例化一個TestForm對象, 然後調用form.is_valid方法判斷數據是否合法。在進行相應的操作。

所以我們閱讀源碼就要從初始化這個位置開始閱讀,因爲這裏纔開始使用我們寫的form表單驗證器。

首先我們找到TestForm的__init__(self)函數, 因爲TestForm是我們自己寫的,我們並沒有寫__init__(self)函數,但是我們是繼承的django.forms.Form這個類,所以我們找到這個類的__init__(self)函數。找到這個類的時候我們發現他只是一個空殼類,什麼都沒有,但是它是繼承django.forms.BaseForm這個類的。最後,我們終於在這個類中中找到了__init__函數了。那麼我們就從這裏開始閱讀嗎?答案是否定的,因爲Form這個類有一個參數,metaclass=DeclarativeFieldsMetaclass, 這個就涉及到元類這一部分的知識了,大家不懂的可以參考一下廖雪峯大佬的講解

這裏我就直接說運行順序了,首先會執行metaclass這個參數指定的類的__new__方法, 然後這個方法會返回一個新的類。相當於metaclass這個參數就是用來動態控制類的創建的。

找到django.forms.DeclarativeFieldsMetaclass__new__方法,開始閱讀源碼。

django.forms.DeclarativeFieldsMetaclass._new_()

from django.forms.fields import Field
from django.forms.widgets import MediaDefiningClass
from collections import OrderedDict

class DeclarativeFieldsMetaclass(MediaDefiningClass):
    def __new__(mcs, name, bases, attrs):
        # 首先來了解這幾個參數的具體含義和值
        # mcs: 因爲我們是實例化的TestForm這個類, 所以mcs就是TestForm這個類
        # name:TestForm的名字,也就是 'TestForm'
        # bases: TestForm的所有父類,一個元祖對象
        # attrs: 一個字典對象,包含着TestForm的所有類屬性,類方法。比如我們定義的name,password,email這些
    
        # 定義一個空列表
        current_fields = []
        # 依次遍歷attrs這個字典的key和value
        for key, value in list(attrs.items()):
            # 如果對應的值是Field的實例化對象,也就是我們自己定義的需要表單驗證的屬性。
            # 因爲所有的字段都是Field的子類
            if isinstance(value, Field):
                # 將key和value組成一個二元祖,並且天驕到current_fields列表中
                current_fields.append((key, value))
                # 在attrs中刪除這個key和對應的值,因爲attrs裏面的key和value最後都會變成新創建的類裏面的屬性和方法的。
                # 我們不需要我們定義的這些字段變成這個類的屬性或者方法,所以就從attrs中刪除了這個字段。
                attrs.pop(key)
        
        # attrs中添加一個新值,key爲declared_fields, value爲OrderedDict(current_fields)
        # OrderedDict:就是轉化爲一個有序字典,
        # current_fields裏面就是我們在TestForm中定義的所有字段
        attrs['declared_fields'] = OrderedDict(current_fields)
    
        # 調用DeclarativeFieldsMetaclass類的父類的__new__方法。傳入對應的參數
        new_class = super(DeclarativeFieldsMetaclass, mcs).__new__(mcs, name, bases, attrs)
        # DeclarativeFieldsMetaclass.__new__代碼如下
        '''
        class MediaDefiningClass(type):
            def __new__(mcs, name, bases, attrs):
                # 這些參數的意思和對應的值就不用 說了吧,就是上面傳入進來的參數。
                # 再次調用MediaDefiningClass的父類的__new__方法,就是動態創建一個類。
                # 因爲MediaDefiningClass這個類的父類是type,在python,type.__new__方法是可以動態創建一個類的。
                # 也就是所有的class都可以看做是type的實例化對象。
                # 這樣我們就根據TestForm中的值,創建好了一個新的類
                new_class = super(MediaDefiningClass, mcs).__new__(mcs, name, bases, attrs)
                
                # 如果'media'這個key不在attrs中
                if 'media' not in attrs:
                    # 綁定media這個屬性爲這個函數的返回值,因爲這個函數和我們後面要閱讀的源碼沒什麼大的關係,
                    # 這裏就不閱讀了,大家感興趣的可以自己看一下
                    new_class.media = media_property(new_class)
        
                # 返回新創建的class
                return new_class
        '''
        
        # 這樣,我們就得到了一個新的類new_class

        # 初始化一個有序字典
        declared_fields = OrderedDict()
        # new_class.__mro__存放着所有的父類,一個元祖對象。大家可以自己打印一下自己寫的類的__mro__這個函數的返回結果
        for base in reversed(new_class.__mro__):
            # 如果當前遍歷的父類包含declared_fields這個類屬性,就更新上面初始化的declared_fields
            # 這樣我們自己寫的TestForm也可以被別人繼承,並且也可以使用我們定義的password,username,email這些表單驗證字段。
            if hasattr(base, 'declared_fields'):
                declared_fields.update(base.declared_fields)
    
            # 遍歷當前父類的__dict__.items(), class.__dict__裝着所有的類屬性, 是一個字典對象
            for attr, value in base.__dict__.items():
                # 如果值爲None, 並且attr(也就是base.__dict__的key)在上面的declared_fields中
                # 從declared_fields刪除這個鍵值對,就是爲了過濾掉一些空字段
                if value is None and attr in declared_fields:
                    declared_fields.pop(attr)
        
        # 上面的for循環完了之後,declared_fields中就是我們在表單中定義的所有字段了            
        
        # 然後給新的類綁定base_fields和declared_fields這兩個類屬性,值都爲declared_fields,
        # 也就是我們定義的所有表單字段
        new_class.base_fields = declared_fields
        new_class.declared_fields = declared_fields
    
        # 再返回這個新的類。
        return new_class

讀完上面的代碼我麼可以發現,django將我們自定義的所有表單字段全部從類屬性中剔除。放在了類屬性base_fields和declared_fields上。

接下來我們就可以開始閱讀__init__函數了.

django.forms.BaseForm._init_

from django.utils.html import html_safe
from django.forms.utils import ErrorList
from .renderers import get_default_renderer
import copy

# 首先我們可以看到這個類被html_safe這個裝飾器裝飾了。
# 這個裝飾器裏面的代碼就不閱讀了,因爲這個是和表單渲染模板相關的,我們只是閱讀表單驗證數據這一部風的代碼。
# 這裏就直接告訴大家這個裝飾器幹了什麼事吧,就是檢查被裝飾的類有沒有__html__和__str__這兩個魔術方法。
# 如果有__html__這個魔術方法,拋出異常
# 如果沒有__str__這個魔術方法,拋出異常
# 然後類的__str__方法,生成新的__str__方法,和__html__方法。
@html_safe
class BaseForm:
    # 初始化一些類屬性
    default_renderer = None
    field_order = None
    prefix = None
    use_required_attribute = True

    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):
        # 我們在實例化TestForm的時候是不是傳入的第一個參數爲request.POST,所以data的值就爲
        # data : request.POST
        # files : request.FILES 這個參數在我們對上傳的文件進行表單驗證的時候,就需要傳入這個數據
        # 只要data和files其中一個不爲None, self.is_bound就爲True
        self.is_bound = data is not None or files is not None
        # self.data爲data或者None
        self.data = {} if data is None else data
        # self.files爲files或者None
        self.files = {} if files is None else files
        self.auto_id = auto_id
        if prefix is not None:
            self.prefix = prefix
        self.initial = initial or {}
        
        # 這裏說一下error_class這個參數,對應的值爲ErrorList
        # 這個是Django自己定義的一個list,繼承list和collections.UserList。
        # collections.UserList的用法大家可以百度一下,其實就是一個list
        # 和普通的list一樣的操作,只是zaiErrorList中django又定義了很多自己的方法
        self.error_class = error_class
        self.label_suffix = label_suffix if label_suffix is not None else _(':')
        self.empty_permitted = empty_permitted
        self._errors = None  
        
        # self.base_fields存放了我們所有的表單驗證字段,這個是在__new__方法裏面綁定的屬性
        # 深拷貝一份這些數據,綁定到self.fields屬性中
        self.fields = copy.deepcopy(self.base_fields)
        self._bound_fields_cache = {}
        
        # 下面的這些屬性也不會影響我麼閱讀表單驗證的代碼,這裏就不一一閱讀了,大家可以自己自己閱讀一下, 還是挺簡單的
        self.order_fields(self.field_order if field_order is None else field_order)

        if use_required_attribute is not None:
            self.use_required_attribute = use_required_attribute

        if self.empty_permitted and self.use_required_attribute:
            raise ValueError(
                'The empty_permitted and use_required_attribute arguments may '
                'not both be True.'
            )

        # 這個屬性和表單的渲染模板相關,這裏也不閱讀了。
        if renderer is None:
            if self.default_renderer is None:
                renderer = get_default_renderer()
            else:
                renderer = self.default_renderer
                if isinstance(self.default_renderer, type):
                    renderer = renderer()
        self.renderer = renderer

上面我們閱讀完了django.forms.BaseForm.__init__這個函數,相當於就初始化完了這個對象了,那麼接下來我們應該開始從哪個位置開始閱讀了呢?

我們想一下,我們初始化完form表單的時候,需要調用form.is_valid()這個函數,然後判斷這個方法的返回值是否爲True。如果爲True,就代表表單驗證成功,否則就是驗證出錯了。

所以我們接下來閱讀form.is_valid()方法。這個方法也在django.forms.BaseForm

TestForm().is_valid()

def is_valid(self):
    # self.is_bound我們在__init__函數中看到了,只要傳入了request.POST或者request.FILES中
    # 的任意一個,這個值就爲True
    
    # 接下來我們看一下self.errors這個屬性
    # 只要這個屬性爲False,那麼這個函數就會返回True,也就是驗證成功,
    # 找到self.error,它其實是一個實例方法,只是被property裝飾器裝飾成了一個屬性
    return self.is_bound and not self.errors

TestForm.errors

@property
def errors(self):
    # 返回一個ErrorDict對象
    # 在init函數中,self._errors被初始化爲了None
    # 所以這個條件會被滿足,執行self.full_clean方法
    if self._errors is None:
        self.full_clean()
    # 返回self._errors的值,如果表單驗證傳入的數據沒有錯誤,那麼這個值就爲空,
    # self.is_valid函數就會返回True,表示數據驗證成功
    return self._errors

TestForm.full_clean()

from django.forms.utils import ErrorDict

def full_clean(self):
    # 初始化一個ErrorDict對象,因爲ErrorDict就是繼承的dict類,所以self._errors就是一個字典
    # 只是比普通的字典多了一些ErrorDict中定義的方法
    self._errors = ErrorDict()
    
    # 如果self.is_bound爲False,那麼表示沒有數據,直接返回
    if not self.is_bound:  
        return
    
    # 定義一個字典self.cleaned_data
    # 看到這裏大家有沒有覺得很熟悉了,我們在使用表單的時候,獲取數據就是從self.clean_data中獲取數據的
    self.cleaned_data = {}
    
    # self.empty_permitted在__init__函數中被初始化爲了False,所以這個條件直接不成立
    # 後面的判斷條件和我們這裏沒有關係,我們就不去閱讀了
    if self.empty_permitted and not self.has_changed():
        return
    
    # 依次執行下面三個函數, 我們在下面依次閱讀這三個函數的源碼
    self._clean_fields()
    self._clean_form()
    
    # 這個函數在BaseForm中也是一個空殼函數, 沒有什麼作用。
    # 主要是用來用於完成表單清洗後執行額外的清洗。用於模型表單中的模型驗證。
    # 我們普通的表單驗證他沒有什麼作用,這裏就不管他 了
    self._post_clean()

TestForm._clean_fields()

from django.forms.fields import FileField
from django.core.exceptions import ValidationError

def _clean_fields(self):
    # self.fields就是我們form表單中我們自定義的字段
    for name, field in self.fields.items():
        # 首先我們得知道name和field對應的是什麼值
        # name:就是我們的字段名稱,比如username,password,email這些
        # field:就是字段對應的對象,如果forms.CharField(), forms.EmailField()等等
        
        # field就是具體字段的實例化屬性,所以我們首先首先找到對應的字段
        # 我們在實例化字段的時候,可以傳入一個disabled這個值,如果不傳,默認就是爲False
        # 所以一般我們的字段都只會進入else裏面,這裏我們就直接看一下self.get_initial_for_field這個函數裏面的代碼吧
        '''
        def get_initial_for_field(self, field, field_name):
            # self.initial在__init__函數中我們初始化爲了一個空字典
            # 調用字典的get方法,第一個參數爲字典的key,第二個參數爲如果字典中沒有這個key,就會返回第二個參數的值 
            # 所以這裏會得到第二個參數的值,因爲self.initial爲一個空字典。
            # value的值就爲field.initial
            # field就是爲每一個具體的字段對象
            # 查看對應的Field類我們可以發現,initial的初始值爲我們傳入的initial這個關鍵字參數。
            # 但是我們實例化field的時候並沒有傳入這個值,所以就爲None。所以這個位置的value也是None
            value = self.initial.get(field_name, field.initial)
            # 如果value可以被調用,就調用value,得到value()的返回值
            if callable(value):
                value = value()
            # 返回value
            return value
        '''
        if field.disabled:
            value = self.get_initial_for_field(field, name)
        else:
            # field.widget也是需要我們初始化字段對象的時候需要傳入的參數,如果沒有傳入,那麼默認的field.widget就爲
            # from django.forms.widgets import TextInput
            # 所以我們得找到這個下面的value_from_datadict方法
            '''
            def value_from_datadict(self, data, files, name):
                return data.get(name)
            '''
            # 我們可以看到這個函數就是返回data.get(name).
            # data就是我們傳入的self.data
            # name爲self.add_prefix(name)
            # self.add_prefix函數的作用就是個name添加前綴,如果我們實例化對象的時候傳入了前綴的話,也就是prefix這個變量
            # 大家可以自己看一下這個函數裏面的代碼,也很簡單,這裏我就直接說這個函數幹了什麼事吧
            # 如果self.prefix這個屬性不爲空的話,就返回`self.prefix-name`
            # 但是我們在初始化的時候傳入了前綴,所有這裏就是返回name,也就是字段的名字
            # self.data在__init__函數中將它的值賦值爲request.POST
            # 所以value的值相當於就是request.POST.get(name)
            # 最後,value也就是從前端傳入的值
            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
        
        # 接下來就是開始驗證數據了
        try:
            # 判斷field是否是FileField的實例化對象,也就是是否爲文件對象,這是對文件字段的驗證
            if isinstance(field, FileField):
                # 這個方法裏面幹了什麼我們上面已經說過了
                initial = self.get_initial_for_field(field, name)
                # 調用field.clean方法,value爲前端傳入的值, field.clean方法我們在下面講解裏面的代碼
                value = field.clean(value, initial)
            # 如果不是文件對象相關字段
            else:
                # 調用field.clean方法,並且傳入前端獲取到的值作爲參數
                value = field.clean(value)
            # 在self.clean_data中添加一個鍵值對,name -> field.clean函數的返回值
            # 這也就是爲什麼我們獲取數據都是從form.clean_data中獲取的原因了
            self.cleaned_data[name] = value
            # 如果我們編寫了clean_<name>這個方法。這就是爲什麼我麼需要對一個字段單獨做一些驗證的時候
            # 直接編寫clean_<name>這個方法就可以了
            if hasattr(self, 'clean_%s' % name):
                # 調用getattr函數,獲取這個方法並執行,得到返回結果
                value = getattr(self, 'clean_%s' % name)()
                # 更改鍵值對,將value設置爲函數返回的結果
                # 所以我們編寫的clean_<name>必須有返回值,不然後面我們就獲取不到相應的值了
                self.cleaned_data[name] = value
        # 捕獲validationError這個異常,這也就是爲什麼我們在自定義驗證的時候,需要拋出這個異常,就能夠在後面得到錯誤信息的原因了
        except ValidationError as e:
            # 執行self.add_erorr方法,下面我們會說到這個函數幹了什麼
            self.add_error(name, e)

TestForm.add_errors()

from django.core.exceptions import ValidationError
NON_FIELD_ERRORS = '__all__'

def add_error(self, field, error):
    # filed : 就是字段的名稱, 如username, password, email ,這些等等
    # error : 一個ValidationError的實例化對象,就是拋出異常的時候實例化的具體對象, 也可以不是

    # 如果error不是ValidationError的實例化對象
    if not isinstance(error, ValidationError):
        # 實例化ValidationError這個對象
        error = ValidationError(error)

    # 如果error擁有error_dict這個屬性或方法
    if hasattr(error, 'error_dict'):
        # 如果field不爲None
        if field is not None:
            # 拋出異常
            raise TypeError(
                "The argument `field` must be `None` when the `error` "
                "argument contains errors for multiple fields."
            )
        # 重新賦值error爲error.error_dict
        else:
            error = error.error_dict
    else:
        # 如果field不爲None,那麼就爲field
        # 否則就爲NON_FIELD_ERRORS, 也就是 '__all__'這個字符竄
        error = {field or NON_FIELD_ERRORS: error.error_list}

    # 依次遍歷error.items() 
    # 在上面的代碼中,已經將error變成字典了
    for field, error_list in error.items():
        # field就是表單中的字段名
        # 如果 field不在self.errors中
        if field not in self.errors:
            # self.fields就包含了所有的字段, 我們可以在 __init__函數中看到
            # 如果field != 'NON_FIELD_ERRORS' 並且不在 self.fields中
            if field != NON_FIELD_ERRORS and field not in self.fields:
                # 拋出ValueError的異常
                raise ValueError(
                    "'%s' has no field named '%s'." % (self.__class__.__name__, field))
            # 下面的代碼就很簡單了,這裏主要說一下self.error_class
            # 在__init__函數中, 我們error_class初始化爲django.forms.utils.ErrorList
            # 大家可以看一下這個裏面的代碼,其實很簡單,這裏就不說了,就是一個多了幾個方法的list
            if field == NON_FIELD_ERRORS:
                self._errors[field] = self.error_class(error_class='nonfield')
            else:
                self._errors[field] = self.error_class()
        
        # self._errors[field] 和 error_list相加起來
        self._errors[field].extend(error_list)
        # 如果field在self.cleaned_data中,就刪除
        if field in self.cleaned_data:
            del self.cleaned_data[field]

django.core.exceptions.ValidationError 類 _init_

class ValidationError(Exception):
    def __init__(self, message, code=None, params=None):
        # 調用父類的 __init__ 方法
        super().__init__(message, code, params)

        # 如果傳入的message是ValidationError類的實例
        if isinstance(message, ValidationError):
            # 如果message有 error_dict這個屬性或值
            if hasattr(message, 'error_dict'):
                # 重新賦值message = message.error_dict
                message = message.error_dict
            # 如果message沒有message這個屬性或方法
            elif not hasattr(message, 'message'):
                # 重新賦值message = message.error_dict
                message = message.error_list
            # 如果都不滿足
            else:
                # 賦值這三個值爲message對象的相關屬性
                message, code, params = message.message, message.code, message.params

        # 如果傳入的是dict字典對象
        if isinstance(message, dict):
            # 初始化一個self.error_dict 字典
            self.error_dict = {}
            # 遍歷傳入的字典
            for field, messages in message.items():
                # 如果messages不是ValidationError實例化對象,
                if not isinstance(messages, ValidationError):
                    # 初始化爲ValidationError的實例對象
                    messages = ValidationError(messages)
                # 添加一個鍵值對 field -> messages.error_list
                self.error_dict[field] = messages.error_list

        # 如果爲一個列表
        elif isinstance(message, list):
            # 初始化一個列表
            self.error_list = []
            # 依次遍歷這個列表
            for message in message:
                # 如果message不爲ValidationError實例對象
                if not isinstance(message, ValidationError):
                    # 實例化這個對象
                    message = ValidationError(message)
                # message有error_dict這個對象或屬性
                if hasattr(message, 'error_dict'):
                    # sum 對一個序列中的每一個值進行相加, 在和第二個參數進行相加
                    # 例如 sum( [[1], [2], [3]] ) => [1, 2, 3]
                    # 例如 sum( [[1], [2], [3]], [4]) => [4, 1, 2, 3]
                    # list.extend 將兩個列表加起來
                    self.error_list.extend(sum(message.error_dict.values(), []))
                else:
                    # 將message.error_list和self.error_list加起來
                    self.error_list.extend(message.error_list)

        # 如果上面的都不是, 依次賦值就行了
        else:
            self.message = message
            self.code = code
            self.params = params
            self.error_list = [self]

field.clean ( django.forms.fields.Field().clean )

因爲field.clean在django.forms.fields.Field這個類中,也就是所有的字段類的父類,所以這裏就只閱讀這個類裏面的clean方法了,其他一些字段類重寫了clean方法,這裏就不一一閱讀了,大家對那個字段的clean方法感興趣,可以自己去閱讀一下。

def clean(self, value):
    # value: 就是我們傳入的從前端獲取到的值
    
    # self.to_python方法基本上大多數字段也重寫這個方法,這裏也不閱讀每一個字段類的to_python方法了。
    # 其實這個方法的作用就是將得到的value轉化爲python中的數據類型
    value = self.to_python(value)
    # 這個方法是驗證字段是否爲空,如果self.required 爲True 並且value爲空,就會拋出一個異常,異常碼爲required
    # 這個方法也是大多數字段類都重寫了的,這裏也只是說了Field中的valid方法
    self.validate(value)
    # 依次調用validators,對value進行驗證,如果驗證出錯,拋出ValidationError,並且給出相應的錯誤異常碼,例如invalid,max_length這些
    self.run_validators(value)
    # 這裏我們閱讀一下run_validators裏面的代碼
    '''
        def run_validators(self, value):
            # 如果value在self.empty_values這個屬性裏面, 直接返回
            # self.empty_values這個屬性的值爲(None, '', [], (), {})
            if value in self.empty_values:
                return
                
            # 定義一個空列表
            errors = []
            # 依次遍歷self.validators這個列表
            # self.validators裏面裝個很多驗證器,有這個字段默認的驗證器,也有我們自己在初始化字段的時候傳入的validators
            # 例如 email = forms.CharField(validators=[validators.EmailValidator(message='請輸入正確的郵箱格式')], validators.RegexValidator(r'1[345678]\d{9}',message='請輸入正確的手機號嗎'), ...])
            # self.validators大家可以在字段的額__init__函數中看到對這個屬性的初始化,這裏就不去查看了
            # 所以這個循環就是依次使用我們指定的驗證器和字段自帶的驗證器來驗證數據
            for v in self.validators:
                try:
                    # 首先我們知道self.validators裏面存儲的是一個個驗證器對象,那麼調用一個實例化對象要想被像函數那樣調用的話。
                    # 就必須重寫 __call__ 方法
                    # 所以這個位置我們想知道這個裏面幹了什麼事情,我們就得依次找到驗證器的__call__方法進行查看代碼
                    # 以爲每個驗證器的代碼都不一樣,這裏也不去閱讀了,大家對哪一個驗證器裏面的驗證過程感興趣,就可以自己去研究一下
                    v(value)
                # 捕獲ValidationError異常
                except ValidationError as e:
                    # 如果這個異常有code這個屬性並且e.code早self.error_messages中
                    # self.error_messages爲一個字典,每一個字段中這個屬性存的值也不一樣,大家可以查看一下對應字段中這個屬性的值
                    # 它的格式爲 {'required':'<提示信息>', 'invalid':'<提示信息>', ...}
                    if hasattr(e, 'code') and e.code in self.error_messages:
                        # 賦值e.message屬性爲self.error_messages[e.code], 也就是相應的提示信息
                        e.message = self.error_messages[e.code]
                        
                    # 擴展errors這個列表
                    errors.extend(e.error_list)
            # 如果這個列表不爲空, 拋出ValidationError異常
            if errors:
                raise ValidationError(errors)
    '''
    
    # 返回value
    return value

TestForm._clean_form()

from django.core.exceptions import ValidationError

def _clean_form(self):
    try:
        # 調用self.clean方法, 這個方法在BaseForm中什麼都沒有做,直接返回self.cleaned_data
        # 但是有時候我們會在我們自己定義的Form中重寫這個方法,就會執行我們自己重寫的clean方法了
        # 所以我們有時候需要驗證一些數據的時候,就會重寫這個方法, cleaned_data爲這個方法的返回值
        cleaned_data = self.clean()
    
    # 捕獲ValidationError異常,然後添加錯誤信息
    except ValidationError as e:
        self.add_error(None, e)
    # else裏面的代碼爲try裏面沒有拋出異常纔會執行
    else:
        # 如果返回值不是None,重新賦值self.cleaned_data爲返回的值
        if cleaned_data is not None:
            self.cleaned_data = cleaned_data

經歷過上面那麼多代碼的閱讀,終於將得到的數據進行了判斷,也就是執行完了form.is_valid()函數。並且能得到一些錯誤信息。
如果表單中的數據沒有錯誤,我們就可以從form.cleaned_data中依次獲取數據進行操作了。

上面就是表單對數據驗證部分的源碼閱讀,內容還是挺多的,如果有什麼寫錯了的地方,希望大家能指正出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章