Blank?=False

ゆるゆる仕事したいフリーランスエンジニアの記事

【Django】ModelFormのバリデーション処理の流れについて

Djangoでモデルクラスにカスタムバリデーションを実装し、 ModelFormのis_valid()でモデルクラス側のバリデータがうまく動かないというのに悩まされたので、 フォームでモデルクラスに実装したバリデータを動かす動作を追ってみました。

バリデーションの実装方法について

Djangoは色々なバリデーション処理の実装方法があります。

Form クラスで実装

Formのcleanで実装する方法は、調べれば色々なサイトで出てきます。

inglow.jp

モデルクラスで実装

モデルで実装する方法もありますが、こちらはあまり情報がありません。

こういうときは公式が一番です。 docs.djangoproject.com

試しに実装してみます。

gist.github.com

Djangoに標準で入っているRegexValidatorのソースコードは下記です。 gist.github.com

__call__メソッドで検証する値をvalueで受け取り、検証を行いNGであれば、 raise ValidationErrorで検証NGを通知します。 もし、検証結果がOKであれば何もしません。

ここから、フォームでモデルのバリデータを呼び出す処理を追っていきます。

ModelFormのバリデーション処理の流れ

今回のモデルフォーム

今回、サンプルとしてFooクラスのモデルフォームをを作りました。

gist.github.com

コレを元に説明していきます。

ModelFormでis_valid()が呼ばれたら

ModelForm

Viewのpostメソッドなりで、ModelFormのis_validを呼び出すと、 ModelFormのスーパークラスであるBaseFormクラスのis_validの処理が行われます。 ※ModelFormはIs_validをオーバーライドしてない

gist.github.com

is_validのself.errorsはerrorsメソッドを呼び出しますが、ここでエラーがまだなければself.full_cleanメソッドをコールして検証を行います。 full_cleanメソッドのself._post_clean()でようやくModelFormでオーバーライドされた処理に移ります。

gist.github.com _post_cleanですが、Modelformのinitinstanceを渡していなかった場合、 ModelFormのMetaクラスに設定したModelのインスタンスを生成してそれをセットしています。 そして、取得したinstanceのfull_cleanメソッドを呼び出します。 このfull_cleanはFormではなく、Modelのfull_cleanです。

ここからようやくモデルのバリデータを動かす処理に入ります。

Model

modelのfull_cleanself.clean_fieldsで各フィールドのcleanを呼び出します。 gist.github.com

CharFieldcleanメソッドをオーバーライドしていないため、Fieldクラスのcleanメソッドがコールされます。 gist.github.com

cleanrun_validatorsメソッドで、実装したすべてのバリデータをコールします。 バリデータがValidationErrorを出力したらエラーメッセージに内容を追加します。

まとめ

  • ModelFormのis_validでモデルクラスに実装したバリデータはinstanceから呼び出される