dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

[django]フォームのバリデーションまとめ(to_python, clean, validate)

django の Form とバリデーションについてまとめてみた。

https://docs.djangoproject.com/en/1.5/ref/forms/validation/

今回使用した django のバージョンは 1.5。

日本語のページ もありますが情報が古い(最新で v1.4) ので、
なるべく英語版の、自分が使っているバージョンにあったものを読んだ方がいいです。

バリデーションの実行順序

以下の順に、フォームおよびフィールドの各メソッドが呼ばれる。

1. <Field>.to_python()

2. <Field>.validate()

  # <Field>.run_validators()
  # <Field>.clean()

3. <Form>.clean_<fieldname>()

4. <Form>.clean()

コメントアウトしたメソッドは 2 と 3 の間で呼び出されるそうだが、使用することは(おそらく)ない。


1. <Field>.to_python()

最初に呼ばれ、フォームなどの入力値そのものを引数として受け取り正しい python のデータ型に変換する。
できなければ ValidationError を raise する。

  • 引数: あり
  • return: 必要


2. <Field>.validate()

1 の to_python によって適切な型に変換された後の値を受け取り、
フィールド固有のバリデーションを行う。
validator ではできない or validator を使いたくないバリデーションのロジックを
このメソッドで記述する。
引数で渡された値の 変更は行うべきでない

  • 引数: あり
  • return: 不要


3. <Form>.clean_<fieldname>()

フォームの特定の属性に対するクリーニング。

  • 引数: なし
    • なので、フォームの self.cleaned_data (dict) から取得する必要がある
  • return: 必要(クリーニングした後の値)


4. <Form>.clean()

最後。フォームの 複数のフィールドにまたがるバリデーションを行いたい場合 はここに記述する。
(具体的には後述)

  • 引数:なし
  • return: 必要(最終的な self.cleaned_data
  • このメソッド内でエラーを raise した場合、(いずれかのフィールドではなく) __all__ という名の特殊なフィールドに関連づけられる。
    エラーは non_field_errors() でアクセス可能

http://docs.djangoproject.jp/en/latest/ref/forms/validation.html

オーバライドされた Form.clean() の送出するエラーは、フォームの いずれかのフィールドではなく、(__all__ という名の) 特殊な「フィールド」に関連づけられます。エラーは non_field_errors() でアクセス できます。エラーをフォームの特定のフィールドに関連付けたいのなら、 後で解説 する、フォームの _errors 属性にアクセスする必要があります。

clean() の例:複数のフィールドにまたがるバリデーション

ref. https://docs.djangoproject.com/en/1.5/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    recipients = MultiEmailField()
    cc_myself = forms.BooleanField(required=False)

    def clean(self):
        cleaned_data = super(ContactForm, self).clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject:
            # Only do something if both fields are valid so far.
            if "help" not in subject:
                raise forms.ValidationError("Did not send for 'help' in "
                        "the subject despite CC'ing yourself.")

        # Always return the full collection of cleaned data.
        return cleaned_data

TODO

  • 1 ~ 4 までのメソッドを含む、簡単なサンプルを書いて試してみる(ユニットテストも含む)
  • バリデーター (validator) について調べる