# Django 4.1.8.dev20230308103347 ドキュメント

# フォームとフィールドの検証

フォームのバリデーションは、データがクリーニング (clean) されるときに実行されます。このプロセスをカスタムしたい場合は、様々な箇所に変更を加えることができ、それぞれが違う目的を持っています。クリーニング方法のうち 3 タイプは、フォームのプロセス中に実行されます。これらは、通常フォーム上の `is_valid()` メソッドを呼び出したときに実行されます。このほかにも、クリーニングとバリデーションのトリガーとなる処理 (直接 `errors` 属性にアクセスしたり、 `full_clean()` を直接呼び出す) がありますが、通常必要とはなりません。

一般的に、あらゆるクリーニング方法は `ValidationError` を投げる可能性があります。処理されるデータに問題がある場合、関連情報を `ValidationError` コンストラクタに渡します。下記項目 に、`ValidationError` を投げる際のベストプラクティスがあります。`ValidationError` が投げられない場合、メソッドはクリーニングされた (標準化された) データを Python オブジェクトとして返すはずです。

Most validation can be done using validators - helpers that can be reused. Validators are functions (or callables) that take a single argument and raise `ValidationError` on invalid input. Validators are run after the field's `to_python` and `validate` methods have been called.

フォームのバリデーションは複数のステップに分割されます。カスタムやオーバーライドすることができます:

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

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

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

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

- `clean_<fieldname>()` メソッドは、フォームサブクラス上で呼び出されます -- `<fieldname>` がフォームのフィールド属性の名前と置き換えられます。このメソッドは、フィールドのタイプにかかわらず、その特定の属性に対するクリーニングを実行します。 このメソッドはパラメータを受け取りません。`self.cleaned_data` 内のフィールドの値を検索する必要があり、またこの時点ではフォーム上で元々送信された文字列ではなく Python オブジェクトであること覚えておいてください (これは `cleaned_data` 内にあります。上記に出てきた一般フィールドの `clean()` メソッドがすでに一度データをクリーニングしているからです)。

  For example, if you wanted to validate that the contents of a `CharField` called `serialnumber` was unique, `clean_serialnumber()` would be the right place to do this. You don't need a specific field (it's a `CharField`), but you want a formfield-specific piece of validation and, possibly, cleaning/normalizing the data.

  このメソッドの戻り値は `cleaned_data` 内の既存の値を置き換えるため、(たとえこのメソッドが変更しなかったとしても) `cleaned_data` からのフィールドの値、ないし新しくクリーニングされた値になるはずです。

- フォームのサブクラスの `clean()` メソッドは、複数のフォームフィールドにまたがるバリデーションにも使用できます。この場所には、たとえば「フィールド `A` が入力されたときフィールド `B` は有効なメールアドレスのはず」といったチェックを記述できます。このメソッドは、必要な場合はまったく違うディクショナリを返すこともでき、その場合は `cleaned_data` として使用されます。

  フィールドバリデーションのメソッドは `clean()` が呼ばれる際に実行されるので、フォームの `errors` 属性にもアクセスできるようになります。これは各フィールドのクリーニングによって発生した例外をすべて含みます。

  `Form.clean()` をオーバーライドして発生させた例外は、特定のフィールドに結びつかない点に注意してください。これらは特別な "フィールド" (`__all__` と呼ばれます) に格納され、必要に応じて `non_field_errors()` メソッドを通じてアクセスできます。特定のフィールドにエラーを紐付けて格納したい場合は、`add_error()` を呼び出す必要があります。

  `ModelForm` サブクラスの `clean()` メソッドをオーバーライドする際、特に考慮する点がまだあります (詳しくは ModelForm ドキュメント を参照してください)。

これらのメソッドは、一度に一つのフィールドに対し、先述した通りの順番で実行されます。フォーム内の各フィールドに対して (フォームの定義時に宣言された順序で)、`Field.clean()` メソッド (ないしそのオーバーライド) が実行され、その後 `clean_<fieldname>()` が実行されます。最後に、この 2 つのメソッドが全フィールドに対して実行された後、これらのメソッドでエラーが発生したかどうかにかかわらず `Form.clean()` メソッド (ないしそのオーバーライド) が実行されます。

各メソッドの例は後述します。

上述の通り、どのメソッドでも `ValidationError` が発生する可能性があります。どのフィールドに対しても、`Field.clean()` メソッドが `ValidationError` を発生させた場合、フィールド特有のクリーニングメソッドは呼ばれません。一方で、すべての残りのフィールドに対するクリーニングメソッドは呼び出されます。

## `ValidationError` を発生させる

エラーメッセージを柔軟かつ簡単にオーバーライドできるようにするため、以下のガイドラインを検討してください:

- 説明のための `code` をコンストラクタに渡します:

      # Good
      ValidationError(_('Invalid value'), code='invalid')

      # Bad
      ValidationError(_('Invalid value'))

- メッセージには変数を強制しません; プレースホルダとコンストラクタの `params` 引数を使用します:

      # Good
      ValidationError(
          _('Invalid value: %(value)s'),
          params={'value': '42'},
      )

      # Bad
      ValidationError(_('Invalid value: %s') % value)

- 位置指定ではなく、マッピングキーを使います。

      # Good
      ValidationError(
          _('Invalid value: %(value)s'),
          params={'value': '42'},
      )

      # Bad
      ValidationError(
          _('Invalid value: %s'),
          params=('42',),
      )

- メッセージを `gettext` でラップし、翻訳できるようにします:

      # Good
      ValidationError(_('Invalid value'))

      # Bad
      ValidationError('Invalid value')

全てを一緒に記述すると以下のようになります:

    raise ValidationError(
        _('Invalid value: %(value)s'),
        code='invalid',
        params={'value': '42'},
    )

再利用可能なフォーム、フォームフィールド、モデルフィールドを記述した場合、特にこのガイドラインの遵守が必要となります。

推奨はされませんが、バリデーションチェーンの最後で (例えばフォームの `clean()` メソッド) エラーメッセージのオーバーライドを 決してしない ことが確かな場合は、より簡潔に記述することもできます:

    ValidationError(_('Invalid value: %s') % value)

`Form.errors.as_data()` や `Form.errors.as_json()` メソッドは、十分な機能を有する (`code` 名と `params` ディクショナリを持つ) `ValidationError` により大きな恩恵を受けます。

### 複数のエラーを起こす

クリーニングメソッド内で複数のエラーを検証し、すべてのエラーをフォーム送信者に知らせたい場合、`ValidationError` コンストラクタにエラーのリストを引き渡すことが可能です。

上述の通り、`ValidationError` インスタンスには `code` と `params` を渡すことが推奨されていますが、文字列のリストも使うことができます:

    # Good
    raise ValidationError([
        ValidationError(_('Error 1'), code='error1'),
        ValidationError(_('Error 2'), code='error2'),
    ])

    # Bad
    raise ValidationError([
        _('Error 1'),
        _('Error 2'),
    ])

## 実際にバリデーションを使用する

前のセクションでは、フォームに対する検証が一般にどのように働くかを説明しました。実際の使われ方を見た方が機能をよく理解できるということが往々にしてあります。ここでは、説明した各機能を使った一連の小さな使用例を説明します。

### バリデータを使う

Django's form (and model) fields support use of utility functions and classes known as validators. A validator is a callable object or function that takes a value and returns nothing if the value is valid or raises a `ValidationError` if not. These can be passed to a field's constructor, via the field's `validators` argument, or defined on the `Field` class itself with the `default_validators` attribute.

Validators can be used to validate values inside the field, let's have a look at Django's `SlugField`:

    from django.core import validators
    from django.forms import CharField

    class SlugField(CharField):
        default_validators = [validators.validate_slug]

As you can see, `SlugField` is a `CharField` with a customized validator that validates that submitted text obeys to some character rules. This can also be done on field definition so:

    slug = forms.SlugField()

これは以下と同じです:

    slug = forms.CharField(validators=[validators.validate_slug])

一般的なケース (例えば、E メールや正規表現に対する検証) は、Django が提供する既存のバリデータクラスを使って処理できます。例えば、`validators.validate_slug` は `RegexValidator` の第 1 引数をパターン `^[-a-zA-Z0-9_]+$` としたインスタンスです。バリデータを記述する のセクションを参照して、利用可能なバリデータのリストとバリデータの記述方法の例を確認できます。

### フォームフィールドのデフォルトのクリーニング

まず、カンマで区切られたメールアドレスを含むかどうか検証するカスタムフォームフィールドを作成しましょう。クラスの全体像は以下のようになります:

    from django import forms
    from django.core.validators import validate_email

    class MultiEmailField(forms.Field):
        def to_python(self, value):
            """Normalize data to a list of strings."""
            # Return an empty list if no input was given.
            if not value:
                return []
            return value.split(',')

        def validate(self, value):
            """Check if value consists only of valid emails."""
            # Use the parent's handling of required fields, etc.
            super().validate(value)
            for email in value:
                validate_email(email)

このフィールドを使うすべてのフォームでは、フィールドのデータを使えるようになる前に、これらのメソッドが実行されます。これはこのタイプのフィールドに特有であり、それはこの後の使われ方には関係ありません。

Let's create a `ContactForm` to demonstrate how you'd use this field:

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

Use `MultiEmailField` like any other form field. When the `is_valid()` method is called on the form, the `MultiEmailField.clean()` method will be run as part of the cleaning process and it will, in turn, call the custom `to_python()` and `validate()` methods.

### 特定のフィールド属性をクリーニングする

上の例を引き続き使用して、`ContactForm` の `recipients` フィールドが常に``"fred@example.com"`` を含むようにしたいとします。これはフォームに特有のバリデーションなので、`MultiEmailField` クラスには記述したくありません。代わりに `recipients` フィールドで動作するクリーニングメソッドを記述します:

    from django import forms
    from django.core.exceptions import ValidationError

    class ContactForm(forms.Form):
        # Everything as before.
        ...

        def clean_recipients(self):
            data = self.cleaned_data['recipients']
            if "fred@example.com" not in data:
                raise ValidationError("You have forgotten about Fred!")

            # Always return a value to use as the new cleaned data, even if
            # this method didn't change it.
            return data

### 互いに依存するフィールドをクリーニングして検証する

コンタクトフォームに新たな要件を追加することを考えます: `cc_myself` フィールドが `True` の場合、`subject` には必ず `"help"` という言葉が含まれるという要件です。この場合、複数のフィールドにまたがったバリデーションを行うので、フォームの `clean()` メソッドで行うのが適切です。ここで注意すべきなのは、今扱っているのはフォームの `clean()` メソッドであり、上記で扱ってきたフィールドの `clean()` ではないということです。バリデーションを記述すする場所を決める際は、フィールドとフォームの違いを明確にすることが重要です。 フィールドは単一のデータポイントであり、フォームはフィールドの集まりです。

フォームの `clean()` メソッドが呼び出されるまでに、個別のフィールドのクリーンメソッドが呼び出されているので (上記 2 つのセクションで見た通りです)、`self.cleaned_data` にはこれまでのクリーニングで生き残ったデータが格納されています。したがって、検証しようとしているフィールドが、この個別フィールドのチェックを生き残っていない可能性を考慮する必要があります。

この段階でのエラーを通知するには 2 つの方法があります。おそらく最も一般的な方法は、フォームのトップでエラーを表示する方法です。このようなエラーを生成するには、`clean()` メソッドで `ValidationError` を発生させてください。例えば:

    from django import forms
    from django.core.exceptions import ValidationError

    class ContactForm(forms.Form):
        # Everything as before.
        ...

        def clean(self):
            cleaned_data = super().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 ValidationError(
                        "Did not send for 'help' in the subject despite "
                        "CC'ing yourself."
                    )

In this code, if the validation error is raised, the form will display an error message at the top of the form (normally) describing the problem. Such errors are non-field errors, which are displayed in the template with `{{ form.non_field_errors }}`.

例のコードにある `super().clean()` の呼び出しは、親クラスのバリデーションロジックも維持されることを保証します。`clean()` メソッドで (必須ではないので) `cleaned_data` ディクショナリを返さないクラスを継承している場合、`cleaned_data` を `super()` の結果にアサインせず、代わりに `self.cleaned_data` を使用してください:

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

バリデーションエラーを通知するもう 1 つのアプローチは、エラーメッセージをフィールドの 1 つにアサインすることです。この場合、エラーメッセージはフォーム表示の "subject" と "cc_myself" の両方の行にアサインしましょう。この方法をとるときは、フォームの出力に混乱を招かないように注意してください。ここでは何が可能なのかを示しますが、実際の状況で効果的に実装するのはあなたとあなたのデザイナー次第です。(上の例を置き換えた) 新しいコードは以下のようになります:

    from django import forms

    class ContactForm(forms.Form):
        # Everything as before.
        ...

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

            if cc_myself and subject and "help" not in subject:
                msg = "Must put 'help' in subject when cc'ing yourself."
                self.add_error('cc_myself', msg)
                self.add_error('subject', msg)

The second argument of `add_error()` can be a string, or preferably an instance of `ValidationError`. See ValidationError を発生させる for more details. Note that `add_error()` automatically removes the field from `cleaned_data`.