星期一, 11月 30, 2015

Django model 的 validate

同事問,為什麼用 model 建立 instance 以後,呼叫 save 沒有觸發 validate?
上網找了一下,大致有兩種方法:
  1. 用 ModelForm 的 is_valid() 做檢查,Correct Way to Validate Django Model Objects?
  2. 覆寫 model 的 save(),Django model mixin to force Django to validate (i.e. call `full_clean`) before `save`
事情沒有這樣結束,因為很好奇 Django 的作法,就去追 Code。
如果是走 is_valid() :
  1. forms/models.py:ModelForm 繼承自 BaseModelForm,而 BaseModelForm 又繼承自 BaseForm,這個檔案裡,沒有看到 is_valid
  2. forms/forms.py:
    1. BaseForm.is_valid() 很簡單的只回傳 self.is_bound and not self.errors ,self.is_bound 跳過不看,self.errors 是一個 property,裏面呼叫了 self.full_clean()
    2. self.full_clean() 呼叫了 self._clean_fields(), self._clean_form() 與 self._post_clean() ,關鍵在 self._post_clean() 。BaseForm 裡的 _post_clean() 是空的,讓繼承 BaseForm 的類別去決定。
  3. forms/models.py: 回頭看 BaseModelForm._post_clean(),這裡呼叫了 self.instance.full_clean() ,並處理 ValidationError 。instance 就是 model 類別所創出的實體,Model 繼承自 ModelBase 所以接著看 ModelBase 類別。
  4. db/models/base.py: ModelBase 的 full_clean() 呼叫了 self.clean_fields(), self.clean() 與 self.validate_unique ,並且在這裡處理了 ValidationError 例外,最後判斷了 errors 裡是否有東西,有的話,丟出 ValidationError。先看 self.clean_fields() ,函式裡去取得定義在類別裡的欄位,並且呼叫每個欄位 (Field) 類別的 clean() 函式。如果有錯誤就丟出 ValidationError()
  5. db/models/fields/__init__.py:Field 類別定義在這個檔案裡,裏面的 clean() 呼叫了 self.validate() 、self.run_validators()
至此,ModelForm 的路徑就確定了。
來看看第二個方法,他是要求 Model 類別除了繼承 Django Model 之外,再繼承 ValidateModelMixin 這個自訂類別以覆寫 save() ,看看裏面的 save(),就只是多呼叫了 full_clean() 來檢查,也就是走上述步驟 4 以後的路線。
Django 官方網站不建議直接去呼叫 full_clean() 這件事,不過找了一下,也沒找到什麼更好的解法~
P.S.

沒有留言: