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