Django 1.7aのForm.add_errorを使ってみる


Djangoのformで複数フィールドにまたがるバリデーションをかけたい場合は、cleanメソッドに処理を書く。


# coding=utf-8
from django import forms
from django.core.exceptions import ValidationError


class PasswordConfirmForm(forms.Form):
    password = forms.CharField()
    password_confirm = forms.CharField()

    def clean(self):
        cleaned_data = super(PasswordConfirmForm, self).clean()

        password = self.cleaned_data.get('password')
        password_confirm = self.cleaned_data.get('password_confirm')

        if password != password_confirm:
            raise ValidationError(u'password is not match.')

        return cleaned_data

cleanメソッドでraiseされたValidationErrorはフィールドに紐付けられず、NON_FIELD_ERRORとして扱われる

>>> from accounts.forms import PasswordConfirmForm
>>> f = PasswordConfirmForm(dict(password='abc', password_confirm='xyz'))
>>> f.is_valid()
False
>>> f.errors
{u'__all__': [u'password is not match.']}
>>> f.non_field_errors()
[u'password is not match.']


cleanメソッドでエラーをフィールドに紐付けたい場合がある。
今まではこんな感じでやっていた。

# coding=utf-8
from django import forms
from django.forms.util import ErrorList


class PasswordConfirmForm(forms.Form):
    password = forms.CharField()
    password_confirm = forms.CharField()

    def clean(self):
        cleaned_data = super(PasswordConfirmForm, self).clean()

        password = self.cleaned_data.get('password')
        password_confirm = self.cleaned_data.get('password_confirm')

        if password != password_confirm:
            errors = self._errors.setdefault("password_confirm", ErrorList())
            errors.append(u'password is not match.')

        return cleaned_data

>>> from accounts.forms import PasswordConfirmForm
>>> f = PasswordConfirmForm(dict(password='abc', password_confirm='xyz'))
>>> f.is_valid()
False
>>> f.errors
{'password_confirm': [u'password is not match.']}
>>> f.non_field_errors()
[]
>>> f['password_confirm'].errors
[u'password is not match.']


Django 1.7a2だとForm.add_errorというメソッドが用意されてるので、面倒なことしなくてもよくなる。

The Forms API ― Django 1.7a2 documentation


サンプルはエラー文字列を渡しているけど、ValidationErrorのインスタンスを渡すのが推奨されているらしい。

# coding=utf-8
from django import forms


class PasswordConfirmForm(forms.Form):
    password = forms.CharField()
    password_confirm = forms.CharField()

    def clean(self):
        cleaned_data = super(PasswordConfirmForm, self).clean()

        password = self.cleaned_data.get('password')
        password_confirm = self.cleaned_data.get('password_confirm')

        if password != password_confirm:
            self.add_error('password_confirm', 'password is not match.')

        return cleaned_data

>>> from accounts.forms import PasswordConfirmForm
>>> f = PasswordConfirmForm(dict(password='abc', password_confirm='xyz'))
>>> f.is_valid()
False
>>> f.errors
{'password_confirm': [u'password is not match.']}