django—Form元件校驗方法(is_valid)執行流程
1、從is_valid方法入手
def is_valid(self):
"""Return True if the form has no errors, or False otherwise."""
return self.is_bound and not self.errors
如果is_valid要返回True的話,那麼self.is_bound要為True並且self.errors要為Flase(字面意思上看errors為False的話,表示沒有錯誤資訊)
2、is_bound
以下程式碼出現在BaseForm類中,也就是Form的父類(Form繼承了父類的初始化方法)
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):
self.is_bound = data is not None or files is not None
is_bound為True的條件是data不為None,或者files不為None
也就是說在初始化一個Form物件時,只要傳入了data或者files,那麼is_bound的屬性就一定為True
在呼叫is_valid方法校驗資料之前,肯定需要將POST提交的資料用來初始化一個Form物件,然後呼叫該方法對資料進行校驗
所以,只要是用來校驗POST提交的資料時,is_bound肯定為True
form_obj = RegForm(request.POST) # 包含使用者提交的資料的From物件
if form_obj.is_valid(): # 對資料進行校驗return HttpResponse('ok')
3、errors
@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
errors實際上是一個方法,只是使用property裝飾器裝飾成了屬性,可以通過物件.屬性名的方式呼叫
當self._errors為None時會執行,full_clean方法。然後最終返回_errors。
_errors同樣定義在BaseForm的初始化方法內,初始化即為None,但是最終返回卻將它作為結果返回。
那麼,說明full_clean內部肯定對_errors做了修改
直接看full_clean方法內部做了些什麼
4、full_clean()
def full_clean(self):
"""
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 = {} # 建立了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()
該方法首先賦值了一個錯誤字典給_errors(前面提到,初始化時為None),也就是說再經過一系列校驗後,如果最後該字典為空,那麼說明沒有錯誤,即校驗通過。
接著往下看。
如果is_bound為False直接終止方法執行,也就是該Form物件內沒有資料,不需要校驗。
然後建立了一個字典cleaned_data,用於存放後續校驗無誤的資料。
從這就可以知道,一個form物件只有在使用了is_valid()方法後,內部才會生成cleaned_data字典。使用is_valid()之前沒有該變數。
然後接著看後續呼叫校驗方法的過程。
5、_clean_fields()
def _clean_fields(self):
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
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) # 將捕獲到的異常新增到錯誤字典中
該方法內部,直接對fields進行迭代取值處理。
fields內部存放的就是表單提交的資料,以字典形式存放,name對應的就是Form類中定義的屬性名,field就是一個欄位物件。
如果當前迭代到的欄位物件對應的input被禁用的話(disableed為True),那麼就以該input的初始值作為該欄位的值(initial設定的值。如果沒有設定,得到的則是空字串)。
反之,該欄位的值就是使用者輸入的值。
接著,判斷當前迭代到的欄位物件型別,如果是檔案型別,則使用檔案相關的校驗方式
其他型別,都使用else程式碼塊裡的校驗方式
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
validate(value)方法,即校驗當前欄位的值是否為空,如果為空則丟擲required的提示資訊(欄位為必填項時會進行該校驗)
run_validators方法,執行的是當前欄位預設的校驗器(如果是Email欄位,則它的預設校驗器就會校驗欄位的值是否符合正確的郵箱格式)以及自定義的校驗器(即設定欄位的引數validators時,傳入的自定義的校驗函式物件)
如果校驗過程中出現異常,會層層丟擲,每一層都在捕獲異常,最終會將捕獲到的異常新增到錯誤欄位中。
except ValidationError as e:
self.add_error(name, e)
然後最終返回原值。如果上述校驗無誤,則將該值新增到cleaned_data字典內。
接著,判斷有沒有該欄位的區域性鉤子函式,對該欄位進行校驗。
有的話,就呼叫這個區域性鉤子就要該欄位的值,校驗無誤就將該值重新賦值一遍給cleaned_data。出現異常就會丟擲,執行異常處理函式,將錯誤資訊新增到錯誤字典,並將該欄位的值從cleaned_data字典內刪除。
從這也就能知道,區域性鉤子函式的返回值必須是對應欄位的值原樣返回,否則即使校驗通過,在賦值時也會出錯,導致最終得到的值不對。
總結:
clean方法主要功能就是執行欄位內建校驗方法、欄位內建校驗器以及自定義校驗器
_clean_fields方法只要功能就是執行clean、以及區域性鉤子函式,對資料進行校驗。
6、_clean_form()
執行全域性的鉤子函式
全域性鉤子函式 ,返回值必須是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__'.
"""
return self.cleaned_data
因為,自定義的全域性鉤子函式,實際上就是重寫了上述方法,最終返回值必須和上述方法用於,即校驗無誤的資料字典
總結:
整個is_valid()的主要的流程為:
1、執行full_clean方法
先定義一個錯誤字典使用者後續判斷校驗是否正確,
還有一個cleaned_data存放後續校驗無誤的資料
2、執行_clean_fields方法
該方法對資料進行除了全域性鉤子函式以外的校驗
先執行欄位內建校驗方法、欄位內建校驗器以及自定義校驗器對資料進行校驗
然後執行區域性鉤子函式對資料進行校驗
3、執行_clean_form方法
該方法對資料進行全域性鉤子函式的校驗
所有校驗執行的先後順序:
欄位內建的校驗方法——>欄位內建校驗器——>欄位自定義的校驗器——>區域性鉤子函式——>全域性鉤子函式