Torekatsuのブログ

日々学んだことを細々と

Djangoのフォームのバリデーション

Django公式ドキュメントに書かれてあるフォームのバリデーションについて簡単にまとめてみました。

フォームとフィールドの検証 | Django ドキュメント | Django

公式ドキュメントに書かれている情報をまとめると フォームのバリデーションは以下の順で実行される。

  1. Formクラスのis_valid()
  2. Fieldクラスのclean()
  3. Fieldクラスのto_python()
  4. Fieldクラスのvalidate()
  5. Fieldクラスのrun_validators()
  6. Formクラスのclean_filed名()
  7. Formクラスのclean()

今回は2~5のFieldクラスのバリデーションについて調べてみました。

Fieldクラスのclean()メソッド

まずは、Fieldクラスのclean()メソッドから見ていきます。
Fieldクラスで実行されるバリデーションのエントリポイントがclean()メソッドです。
以下に公式ドキュメントを引用します。

Field サブクラスの clean() メソッドは、to_python()、validate()、run_validators() を正しい順序で実行し、エラーを伝達する役目を負っています。もしこの過程のどこかで ValidationError が投げられた場合、バリデーションは停止し、そのエラーを投げます。このメソッドはクリーニングされたデータを返し、フォームの cleaned_data ディクショナリに格納します。

コードはこんな感じで、
公式ドキュメントの記載通りto_python(), validate(), run_validators()を順に実行しています。

django/fields.py at 98f23a8af0be7e87535426c5c83058e2682bfdf8 · django/django · GitHub

def clean(self, value):
    """
    Validate the given value and return its "cleaned" value as an
    appropriate Python object. Raise ValidationError for any errors.
    """
    value = self.to_python(value)
    self.validate(value)
    self.run_validators(value)
    return value

Fieldクラスのto_python()メソッド

次にto_python()メソッドです。

Field の to_python()メソッドはすべてのバリデーションの最初のステップとなります。これは、値をデータ型に正すことを強制し、それができない場合は ValidationError を投げます。このメソッドはウィジェットからそのままの値を受け取り、変換した値を返します。たとえば、FloatField は Python の float に変換されるか、もしくは ValidationError を投げます。

フィールドの値をPythonの型に変換することでバリデーションを行うみたいです。

django/fields.py at 98f23a8af0be7e87535426c5c83058e2682bfdf8 · django/django · GitHub

def to_python(self, value):
    return value

Fieldクラスでは単に引数をreturnしてますね。
このメソッドはFieldクラスのサブクラスでオーバライドされる事が前提みたいです。
参考例としてCharFieldでオーバライドされたto_python()メソッドを見てみましょう。

django/fields.py at 98f23a8af0be7e87535426c5c83058e2682bfdf8 · django/django · GitHub

def to_python(self, value):
    """Return a string."""
    if value not in self.empty_values:
        value = str(value)
        if self.strip:
            value = value.strip()
    if value in self.empty_values:
        return self.empty_value
    return value

CharFieldのto_python()メソッドでは以下の実装となっています。

  • valueが(None, '', , (), {})でなければ文字列型に変換し、strip()で空白文字を削除しreturn
  • valueが(None, '', , (), {})なら、''をreturn

Fieldクラスのvalidate()メソッド

次にvalidate()メソッドです。

Field の validate()メソッドは、フィールド特有のバリデーションを行います。これはバリデータとしては不適です。正しいデータ型を強制された値を取り、エラーの際は ValidationError を投げます。このメソッドは何も返さず、また値の変更も行わないはずです。バリデータに記述したくないバリデーションロジックを実行するためには、このメソッドをオーバーライドしてください。

うーん、このメソッドはぶっちゃけ何のためにあるのかよくわからないです。。。

django/fields.py at 98f23a8af0be7e87535426c5c83058e2682bfdf8 · django/django · GitHub

def validate(self, value):
    if value in self.empty_values and self.required:
        raise ValidationError(self.error_messages['required'], code='required')

Fieldクラスのvalidate()メソッドでは

  • valueが(None, '', [], (), {})であるか
  • フィールドがrequiredであるか を元にバリデーションを行っています。

このvalidate()メソッドもFiledのサブクラス側でオーバライドされている事があります。

Fieldクラスのrun_validators()メソッド

最後にrun_validators()メソッドです。

Field の run_validators() メソッドはフィールドのバリデータをすべて実行し、すべてのエラーを単一の ValidationError に統合します。このメソッドをオーバーライドする必要はないはずです。

Fieldに設定されたバリデータを全て実行するメソッドで、
基本的にオーバライドはしないメソッドのようです。

django/fields.py at 98f23a8af0be7e87535426c5c83058e2682bfdf8 · django/django · GitHub

def run_validators(self, value):
    if value in self.empty_values:
        return
    errors = []
    for v in self.validators:
        try:
            v(value)
        except ValidationError as e:
            if hasattr(e, 'code') and e.code in self.error_messages:
                e.message = self.error_messages[e.code]
            errors.extend(e.error_list)
    if errors:
        raise ValidationError(errors)

forループで全てのバリデータを実行していますね。

次回はModelFormのバリデーションについて書きたいと思います。