DjangoのFormのcleanメソッドの呼び出し順
DjangoのFormでis_validが呼ばれたときの処理を読んでたのでメモ
まず、データが渡されたFormのインスタンスでis_validが呼ばれた場合、内部でfull_cleanがよばれる。
該当の箇所
django/forms/forms.py
def _get_errors(self): "Returns an ErrorDict for the data provided for the form" if self._errors is None: self.full_clean() return self._errors errors = property(_get_errors) def is_valid(self): """ Returns True if the form has no errors. Otherwise, False. If errors are being ignored, returns False. """ return self.is_bound and not bool(self.errors)
full_cleanは内部で_clean_fieldsを呼び、ここで各fieldのcleanメソッドが呼ばれる。
cleanが返した値はself.cleaned_dataに保存される
続いてclean_xxxxが定義されている場合は、呼び出しを行う。
full_cleanと_clean_fieldsの定義
django/forms/forms.py
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() 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. value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): initial = self.initial.get(name, field.initial) 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._errors[name] = self.error_class(e.messages) if name in self.cleaned_data: del self.cleaned_data[name]
次にfull_cleanから_clean_formが呼ばれる
ここで呼ばれるのがformのcleanメソッド。
エラーはNON_FIELD_ERRORSとしてセットされる。
django/forms/forms.py
def _clean_form(self): try: self.cleaned_data = self.clean() except ValidationError as e: self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
最後にfull_cleanから_post_cleanが呼ばれる。
ModelFormである場合は、Modelに実装したcleanメソッドが呼ばれるが、forms.Formの場合は何もしない
_post_cleanの定義
django/forms/forms.py
def _post_clean(self): """ An internal hook for performing additional cleaning after form cleaning is complete. Used for model validation in model forms. """ pass
printデバッグで呼び出し順をみてみる
class SampleFormBase(forms.Form): def is_valid(self): print "is_valid" return super(SampleFormBase, self).is_valid() def full_clean(self): print "\tfull_clean" super(SampleFormBase, self).full_clean() def _clean_fields(self): print "\t\t_clean_fields" super(SampleFormBase, self)._clean_fields() def _clean_form(self): print "\t\t_clean_form" super(SampleFormBase, self)._clean_form() def _post_clean(self): print "\t\t_post_clean" super(SampleFormBase, self)._post_clean() class SampleField(forms.CharField): def clean(self, value): print "\t\t\tSampleField.clean value={0}".format(value) return super(SampleField, self).clean(value) class SampleForm(SampleFormBase): param1 = SampleField() param2 = SampleField() def clean_param1(self): print "\t\t\tSampleForm clean_param1" return self.cleaned_data['param1'] def clean_param2(self): print "\t\t\tSampleFormclean_param2" return self.cleaned_data['param2'] def clean(self): print "\t\t\tSampleForm clean" return self.cleaned_data
# is_boundでないのでcleanは呼ばれない >>> f = SampleForm() >>> f.is_valid() False # is_boundの場合 >>> f = SampleForm({'param1': 'param1', 'param2': 'param2'}) >>> f.is_valid() is_valid full_clean _clean_fields SampleField.clean value=param1 SampleForm clean_param1 SampleField.clean value=param2 SampleFormclean_param2 _clean_form SampleForm clean _post_clean True