Fixed #20867 -- Added the Form.add_error() method.
Refs #20199 #16986. Thanks @akaariai, @bmispelon, @mjtamlyn, @timgraham for the reviews.
This commit is contained in:
parent
7e2d61a972
commit
f563c339ca
|
@ -77,64 +77,78 @@ class ValidationError(Exception):
|
||||||
"""An error while validating data."""
|
"""An error while validating data."""
|
||||||
def __init__(self, message, code=None, params=None):
|
def __init__(self, message, code=None, params=None):
|
||||||
"""
|
"""
|
||||||
ValidationError can be passed any object that can be printed (usually
|
The `message` argument can be a single error, a list of errors, or a
|
||||||
a string), a list of objects or a dictionary.
|
dictionary that maps field names to lists of errors. What we define as
|
||||||
|
an "error" can be either a simple string or an instance of
|
||||||
|
ValidationError with its message attribute set, and what we define as
|
||||||
|
list or dictionary can be an actual `list` or `dict` or an instance
|
||||||
|
of ValidationError with its `error_list` or `error_dict` attribute set.
|
||||||
"""
|
"""
|
||||||
if isinstance(message, dict):
|
if isinstance(message, ValidationError):
|
||||||
self.error_dict = message
|
if hasattr(message, 'error_dict'):
|
||||||
elif isinstance(message, list):
|
message = message.error_dict
|
||||||
self.error_list = message
|
elif not hasattr(message, 'message'):
|
||||||
|
message = message.error_list
|
||||||
else:
|
else:
|
||||||
|
message, code, params = message.message, message.code, message.params
|
||||||
|
|
||||||
|
if isinstance(message, dict):
|
||||||
|
self.error_dict = {}
|
||||||
|
for field, messages in message.items():
|
||||||
|
if not isinstance(messages, ValidationError):
|
||||||
|
messages = ValidationError(messages)
|
||||||
|
self.error_dict[field] = messages.error_list
|
||||||
|
|
||||||
|
elif isinstance(message, list):
|
||||||
|
self.error_list = []
|
||||||
|
for message in message:
|
||||||
|
# Normalize plain strings to instances of ValidationError.
|
||||||
|
if not isinstance(message, ValidationError):
|
||||||
|
message = ValidationError(message)
|
||||||
|
self.error_list.extend(message.error_list)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.message = message
|
||||||
self.code = code
|
self.code = code
|
||||||
self.params = params
|
self.params = params
|
||||||
self.message = message
|
|
||||||
self.error_list = [self]
|
self.error_list = [self]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def message_dict(self):
|
def message_dict(self):
|
||||||
message_dict = {}
|
return dict(self)
|
||||||
for field, messages in self.error_dict.items():
|
|
||||||
message_dict[field] = []
|
|
||||||
for message in messages:
|
|
||||||
if isinstance(message, ValidationError):
|
|
||||||
message_dict[field].extend(message.messages)
|
|
||||||
else:
|
|
||||||
message_dict[field].append(force_text(message))
|
|
||||||
return message_dict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def messages(self):
|
def messages(self):
|
||||||
if hasattr(self, 'error_dict'):
|
if hasattr(self, 'error_dict'):
|
||||||
message_list = reduce(operator.add, self.error_dict.values())
|
return reduce(operator.add, dict(self).values())
|
||||||
else:
|
return list(self)
|
||||||
message_list = self.error_list
|
|
||||||
|
|
||||||
messages = []
|
|
||||||
for message in message_list:
|
|
||||||
if isinstance(message, ValidationError):
|
|
||||||
params = message.params
|
|
||||||
message = message.message
|
|
||||||
if params:
|
|
||||||
message %= params
|
|
||||||
message = force_text(message)
|
|
||||||
messages.append(message)
|
|
||||||
return messages
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if hasattr(self, 'error_dict'):
|
|
||||||
return repr(self.message_dict)
|
|
||||||
return repr(self.messages)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'ValidationError(%s)' % self
|
|
||||||
|
|
||||||
def update_error_dict(self, error_dict):
|
def update_error_dict(self, error_dict):
|
||||||
if hasattr(self, 'error_dict'):
|
if hasattr(self, 'error_dict'):
|
||||||
if error_dict:
|
if error_dict:
|
||||||
for k, v in self.error_dict.items():
|
for field, errors in self.error_dict.items():
|
||||||
error_dict.setdefault(k, []).extend(v)
|
error_dict.setdefault(field, []).extend(errors)
|
||||||
else:
|
else:
|
||||||
error_dict = self.error_dict
|
error_dict = self.error_dict
|
||||||
else:
|
else:
|
||||||
error_dict[NON_FIELD_ERRORS] = self.error_list
|
error_dict[NON_FIELD_ERRORS] = self.error_list
|
||||||
return error_dict
|
return error_dict
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
if hasattr(self, 'error_dict'):
|
||||||
|
for field, errors in self.error_dict.items():
|
||||||
|
yield field, list(ValidationError(errors))
|
||||||
|
else:
|
||||||
|
for error in self.error_list:
|
||||||
|
message = error.message
|
||||||
|
if error.params:
|
||||||
|
message %= error.params
|
||||||
|
yield force_text(message)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if hasattr(self, 'error_dict'):
|
||||||
|
return repr(dict(self))
|
||||||
|
return repr(list(self))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'ValidationError(%s)' % self
|
||||||
|
|
|
@ -987,7 +987,7 @@ class Model(six.with_metaclass(ModelBase)):
|
||||||
|
|
||||||
def clean_fields(self, exclude=None):
|
def clean_fields(self, exclude=None):
|
||||||
"""
|
"""
|
||||||
Cleans all fields and raises a ValidationError containing message_dict
|
Cleans all fields and raises a ValidationError containing a dict
|
||||||
of all validation errors if any occur.
|
of all validation errors if any occur.
|
||||||
"""
|
"""
|
||||||
if exclude is None:
|
if exclude is None:
|
||||||
|
|
|
@ -290,6 +290,51 @@ class BaseForm(object):
|
||||||
prefix = self.add_prefix(fieldname)
|
prefix = self.add_prefix(fieldname)
|
||||||
return field.widget.value_from_datadict(self.data, self.files, prefix)
|
return field.widget.value_from_datadict(self.data, self.files, prefix)
|
||||||
|
|
||||||
|
def add_error(self, field, error):
|
||||||
|
"""
|
||||||
|
Update the content of `self._errors`.
|
||||||
|
|
||||||
|
The `field` argument is the name of the field to which the errors
|
||||||
|
should be added. If its value is None the errors will be treated as
|
||||||
|
NON_FIELD_ERRORS.
|
||||||
|
|
||||||
|
The `error` argument can be a single error, a list of errors, or a
|
||||||
|
dictionary that maps field names to lists of errors. What we define as
|
||||||
|
an "error" can be either a simple string or an instance of
|
||||||
|
ValidationError with its message attribute set and what we define as
|
||||||
|
list or dictionary can be an actual `list` or `dict` or an instance
|
||||||
|
of ValidationError with its `error_list` or `error_dict` attribute set.
|
||||||
|
|
||||||
|
If `error` is a dictionary, the `field` argument *must* be None and
|
||||||
|
errors will be added to the fields that correspond to the keys of the
|
||||||
|
dictionary.
|
||||||
|
"""
|
||||||
|
if not isinstance(error, ValidationError):
|
||||||
|
# Normalize to ValidationError and let its constructor
|
||||||
|
# do the hard work of making sense of the input.
|
||||||
|
error = ValidationError(error)
|
||||||
|
|
||||||
|
if hasattr(error, 'error_dict'):
|
||||||
|
if field is not None:
|
||||||
|
raise TypeError(
|
||||||
|
"The argument `field` must be `None` when the `error` "
|
||||||
|
"argument contains errors for multiple fields."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
error = dict(error)
|
||||||
|
else:
|
||||||
|
error = {field or NON_FIELD_ERRORS: list(error)}
|
||||||
|
|
||||||
|
for field, error_list in error.items():
|
||||||
|
if field not in self.errors:
|
||||||
|
if field != NON_FIELD_ERRORS and field not in self.fields:
|
||||||
|
raise ValueError(
|
||||||
|
"'%s' has no field named '%s'." % (self.__class__.__name__, field))
|
||||||
|
self._errors[field] = self.error_class()
|
||||||
|
self._errors[field].extend(error_list)
|
||||||
|
if field in self.cleaned_data:
|
||||||
|
del self.cleaned_data[field]
|
||||||
|
|
||||||
def full_clean(self):
|
def full_clean(self):
|
||||||
"""
|
"""
|
||||||
Cleans all of self.data and populates self._errors and
|
Cleans all of self.data and populates self._errors and
|
||||||
|
@ -303,6 +348,7 @@ class BaseForm(object):
|
||||||
# changed from the initial data, short circuit any validation.
|
# changed from the initial data, short circuit any validation.
|
||||||
if self.empty_permitted and not self.has_changed():
|
if self.empty_permitted and not self.has_changed():
|
||||||
return
|
return
|
||||||
|
|
||||||
self._clean_fields()
|
self._clean_fields()
|
||||||
self._clean_form()
|
self._clean_form()
|
||||||
self._post_clean()
|
self._post_clean()
|
||||||
|
@ -324,15 +370,13 @@ class BaseForm(object):
|
||||||
value = getattr(self, 'clean_%s' % name)()
|
value = getattr(self, 'clean_%s' % name)()
|
||||||
self.cleaned_data[name] = value
|
self.cleaned_data[name] = value
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self._errors[name] = self.error_class(e.messages)
|
self.add_error(name, e)
|
||||||
if name in self.cleaned_data:
|
|
||||||
del self.cleaned_data[name]
|
|
||||||
|
|
||||||
def _clean_form(self):
|
def _clean_form(self):
|
||||||
try:
|
try:
|
||||||
cleaned_data = self.clean()
|
cleaned_data = self.clean()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
|
self.add_error(None, e)
|
||||||
else:
|
else:
|
||||||
if cleaned_data is not None:
|
if cleaned_data is not None:
|
||||||
self.cleaned_data = cleaned_data
|
self.cleaned_data = cleaned_data
|
||||||
|
|
|
@ -326,27 +326,6 @@ class BaseModelForm(BaseForm):
|
||||||
super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
|
super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
|
||||||
error_class, label_suffix, empty_permitted)
|
error_class, label_suffix, empty_permitted)
|
||||||
|
|
||||||
def _update_errors(self, errors):
|
|
||||||
for field, messages in errors.error_dict.items():
|
|
||||||
if field not in self.fields:
|
|
||||||
continue
|
|
||||||
field = self.fields[field]
|
|
||||||
for message in messages:
|
|
||||||
if isinstance(message, ValidationError):
|
|
||||||
if message.code in field.error_messages:
|
|
||||||
message.message = field.error_messages[message.code]
|
|
||||||
|
|
||||||
message_dict = errors.message_dict
|
|
||||||
for k, v in message_dict.items():
|
|
||||||
if k != NON_FIELD_ERRORS:
|
|
||||||
self._errors.setdefault(k, self.error_class()).extend(v)
|
|
||||||
# Remove the data from the cleaned_data dict since it was invalid
|
|
||||||
if k in self.cleaned_data:
|
|
||||||
del self.cleaned_data[k]
|
|
||||||
if NON_FIELD_ERRORS in message_dict:
|
|
||||||
messages = message_dict[NON_FIELD_ERRORS]
|
|
||||||
self._errors.setdefault(NON_FIELD_ERRORS, self.error_class()).extend(messages)
|
|
||||||
|
|
||||||
def _get_validation_exclusions(self):
|
def _get_validation_exclusions(self):
|
||||||
"""
|
"""
|
||||||
For backwards-compatibility, several types of fields need to be
|
For backwards-compatibility, several types of fields need to be
|
||||||
|
@ -393,6 +372,20 @@ class BaseModelForm(BaseForm):
|
||||||
self._validate_unique = True
|
self._validate_unique = True
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
|
def _update_errors(self, errors):
|
||||||
|
# Override any validation error messages defined at the model level
|
||||||
|
# with those defined on the form fields.
|
||||||
|
for field, messages in errors.error_dict.items():
|
||||||
|
if field not in self.fields:
|
||||||
|
continue
|
||||||
|
field = self.fields[field]
|
||||||
|
for message in messages:
|
||||||
|
if (isinstance(message, ValidationError) and
|
||||||
|
message.code in field.error_messages):
|
||||||
|
message.message = field.error_messages[message.code]
|
||||||
|
|
||||||
|
self.add_error(None, errors)
|
||||||
|
|
||||||
def _post_clean(self):
|
def _post_clean(self):
|
||||||
opts = self._meta
|
opts = self._meta
|
||||||
# Update the model instance with self.cleaned_data.
|
# Update the model instance with self.cleaned_data.
|
||||||
|
@ -407,13 +400,12 @@ class BaseModelForm(BaseForm):
|
||||||
# object being referred to may not yet fully exist (#12749).
|
# object being referred to may not yet fully exist (#12749).
|
||||||
# However, these fields *must* be included in uniqueness checks,
|
# However, these fields *must* be included in uniqueness checks,
|
||||||
# so this can't be part of _get_validation_exclusions().
|
# so this can't be part of _get_validation_exclusions().
|
||||||
for f_name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
if isinstance(field, InlineForeignKeyField):
|
if isinstance(field, InlineForeignKeyField):
|
||||||
exclude.append(f_name)
|
exclude.append(name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.instance.full_clean(exclude=exclude,
|
self.instance.full_clean(exclude=exclude, validate_unique=False)
|
||||||
validate_unique=False)
|
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self._update_errors(e)
|
self._update_errors(e)
|
||||||
|
|
||||||
|
@ -695,6 +687,7 @@ class BaseModelFormSet(BaseFormSet):
|
||||||
del form.cleaned_data[field]
|
del form.cleaned_data[field]
|
||||||
# mark the data as seen
|
# mark the data as seen
|
||||||
seen_data.add(data)
|
seen_data.add(data)
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,26 @@ The validation routines will only get called once, regardless of how many times
|
||||||
you access :attr:`~Form.errors` or call :meth:`~Form.is_valid`. This means that
|
you access :attr:`~Form.errors` or call :meth:`~Form.is_valid`. This means that
|
||||||
if validation has side effects, those side effects will only be triggered once.
|
if validation has side effects, those side effects will only be triggered once.
|
||||||
|
|
||||||
|
.. method:: Form.add_error(field, error)
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
This method allows adding errors to specific fields from within the
|
||||||
|
``Form.clean()`` method, or from outside the form altogether; for instance
|
||||||
|
from a view. This is a better alternative to fiddling directly with
|
||||||
|
``Form._errors`` as described in :ref:`modifying-field-errors`.
|
||||||
|
|
||||||
|
The ``field`` argument is the name of the field to which the errors
|
||||||
|
should be added. If its value is ``None`` the error will be treated as
|
||||||
|
a non-field error as returned by ``Form.non_field_errors()``.
|
||||||
|
|
||||||
|
The ``error`` argument can be a simple string, or preferably an instance of
|
||||||
|
``ValidationError``. See :ref:`raising-validation-error` for best practices
|
||||||
|
when defining form errors.
|
||||||
|
|
||||||
|
Note that ``Form.add_error()`` automatically removes the relevant field from
|
||||||
|
``cleaned_data``.
|
||||||
|
|
||||||
Behavior of unbound forms
|
Behavior of unbound forms
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -464,3 +464,31 @@ Secondly, once we have decided that the combined data in the two fields we are
|
||||||
considering aren't valid, we must remember to remove them from the
|
considering aren't valid, we must remember to remove them from the
|
||||||
``cleaned_data``. `cleaned_data`` is present even if the form doesn't
|
``cleaned_data``. `cleaned_data`` is present even if the form doesn't
|
||||||
validate, but it contains only field values that did validate.
|
validate, but it contains only field values that did validate.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
In lieu of manipulating ``_errors`` directly, it's now possible to add errors
|
||||||
|
to specific fields with :meth:`django.forms.Form.add_error()`::
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
class ContactForm(forms.Form):
|
||||||
|
# Everything as before.
|
||||||
|
...
|
||||||
|
|
||||||
|
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 and "help" not in subject:
|
||||||
|
msg = u"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 simple string, or preferably
|
||||||
|
an instance of ``ValidationError``. See :ref:`raising-validation-error` for
|
||||||
|
more details.
|
||||||
|
|
||||||
|
Unlike the ``_errors`` approach, ``add_error()` automatically removes the field
|
||||||
|
from ``cleaned_data``.
|
||||||
|
|
|
@ -350,6 +350,9 @@ Forms
|
||||||
* It's now possible to opt-out from a ``Form`` field declared in a parent class
|
* It's now possible to opt-out from a ``Form`` field declared in a parent class
|
||||||
by shadowing it with a non-``Field`` value.
|
by shadowing it with a non-``Field`` value.
|
||||||
|
|
||||||
|
* The new :meth:`~django.forms.Form.add_error()` method allows adding errors
|
||||||
|
to specific form fields.
|
||||||
|
|
||||||
Internationalization
|
Internationalization
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -657,25 +657,49 @@ class FormsTestCase(TestCase):
|
||||||
self.assertEqual(f.cleaned_data['password2'], 'foo')
|
self.assertEqual(f.cleaned_data['password2'], 'foo')
|
||||||
|
|
||||||
# Another way of doing multiple-field validation is by implementing the
|
# Another way of doing multiple-field validation is by implementing the
|
||||||
# Form's clean() method. If you do this, any ValidationError raised by that
|
# Form's clean() method. Usually ValidationError raised by that method
|
||||||
# method will not be associated with a particular field; it will have a
|
# will not be associated with a particular field and will have a
|
||||||
# special-case association with the field named '__all__'.
|
# special-case association with the field named '__all__'. It's
|
||||||
# Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of
|
# possible to associate the errors to particular field with the
|
||||||
# all the fields/values that have *not* raised a ValidationError. Also note
|
# Form.add_error() method or by passing a dictionary that maps each
|
||||||
# Form.clean() is required to return a dictionary of all clean data.
|
# field to one or more errors.
|
||||||
|
#
|
||||||
|
# Note that in Form.clean(), you have access to self.cleaned_data, a
|
||||||
|
# dictionary of all the fields/values that have *not* raised a
|
||||||
|
# ValidationError. Also note Form.clean() is required to return a
|
||||||
|
# dictionary of all clean data.
|
||||||
class UserRegistration(Form):
|
class UserRegistration(Form):
|
||||||
username = CharField(max_length=10)
|
username = CharField(max_length=10)
|
||||||
password1 = CharField(widget=PasswordInput)
|
password1 = CharField(widget=PasswordInput)
|
||||||
password2 = CharField(widget=PasswordInput)
|
password2 = CharField(widget=PasswordInput)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
# Test raising a ValidationError as NON_FIELD_ERRORS.
|
||||||
if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
|
if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
|
||||||
raise ValidationError('Please make sure your passwords match.')
|
raise ValidationError('Please make sure your passwords match.')
|
||||||
|
|
||||||
|
# Test raising ValidationError that targets multiple fields.
|
||||||
|
errors = {}
|
||||||
|
if self.cleaned_data.get('password1') == 'FORBIDDEN_VALUE':
|
||||||
|
errors['password1'] = 'Forbidden value.'
|
||||||
|
if self.cleaned_data.get('password2') == 'FORBIDDEN_VALUE':
|
||||||
|
errors['password2'] = ['Forbidden value.']
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
# Test Form.add_error()
|
||||||
|
if self.cleaned_data.get('password1') == 'FORBIDDEN_VALUE2':
|
||||||
|
self.add_error(None, 'Non-field error 1.')
|
||||||
|
self.add_error('password1', 'Forbidden value 2.')
|
||||||
|
if self.cleaned_data.get('password2') == 'FORBIDDEN_VALUE2':
|
||||||
|
self.add_error('password2', 'Forbidden value 2.')
|
||||||
|
raise ValidationError('Non-field error 2.')
|
||||||
|
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
f = UserRegistration(auto_id=False)
|
f = UserRegistration(auto_id=False)
|
||||||
self.assertEqual(f.errors, {})
|
self.assertEqual(f.errors, {})
|
||||||
|
|
||||||
f = UserRegistration({}, auto_id=False)
|
f = UserRegistration({}, auto_id=False)
|
||||||
self.assertHTMLEqual(f.as_table(), """<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr>
|
self.assertHTMLEqual(f.as_table(), """<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr>
|
||||||
<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr>
|
<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr>
|
||||||
|
@ -683,6 +707,7 @@ class FormsTestCase(TestCase):
|
||||||
self.assertEqual(f.errors['username'], ['This field is required.'])
|
self.assertEqual(f.errors['username'], ['This field is required.'])
|
||||||
self.assertEqual(f.errors['password1'], ['This field is required.'])
|
self.assertEqual(f.errors['password1'], ['This field is required.'])
|
||||||
self.assertEqual(f.errors['password2'], ['This field is required.'])
|
self.assertEqual(f.errors['password2'], ['This field is required.'])
|
||||||
|
|
||||||
f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
|
f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
|
||||||
self.assertEqual(f.errors['__all__'], ['Please make sure your passwords match.'])
|
self.assertEqual(f.errors['__all__'], ['Please make sure your passwords match.'])
|
||||||
self.assertHTMLEqual(f.as_table(), """<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
self.assertHTMLEqual(f.as_table(), """<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
||||||
|
@ -693,12 +718,25 @@ class FormsTestCase(TestCase):
|
||||||
<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
|
<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
|
||||||
<li>Password1: <input type="password" name="password1" /></li>
|
<li>Password1: <input type="password" name="password1" /></li>
|
||||||
<li>Password2: <input type="password" name="password2" /></li>""")
|
<li>Password2: <input type="password" name="password2" /></li>""")
|
||||||
|
|
||||||
f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
self.assertEqual(f.errors, {})
|
self.assertEqual(f.errors, {})
|
||||||
self.assertEqual(f.cleaned_data['username'], 'adrian')
|
self.assertEqual(f.cleaned_data['username'], 'adrian')
|
||||||
self.assertEqual(f.cleaned_data['password1'], 'foo')
|
self.assertEqual(f.cleaned_data['password1'], 'foo')
|
||||||
self.assertEqual(f.cleaned_data['password2'], 'foo')
|
self.assertEqual(f.cleaned_data['password2'], 'foo')
|
||||||
|
|
||||||
|
f = UserRegistration({'username': 'adrian', 'password1': 'FORBIDDEN_VALUE', 'password2': 'FORBIDDEN_VALUE'}, auto_id=False)
|
||||||
|
self.assertEqual(f.errors['password1'], ['Forbidden value.'])
|
||||||
|
self.assertEqual(f.errors['password2'], ['Forbidden value.'])
|
||||||
|
|
||||||
|
f = UserRegistration({'username': 'adrian', 'password1': 'FORBIDDEN_VALUE2', 'password2': 'FORBIDDEN_VALUE2'}, auto_id=False)
|
||||||
|
self.assertEqual(f.errors['__all__'], ['Non-field error 1.', 'Non-field error 2.'])
|
||||||
|
self.assertEqual(f.errors['password1'], ['Forbidden value 2.'])
|
||||||
|
self.assertEqual(f.errors['password2'], ['Forbidden value 2.'])
|
||||||
|
|
||||||
|
with six.assertRaisesRegex(self, ValueError, "has no field named"):
|
||||||
|
f.add_error('missing_field', 'Some error.')
|
||||||
|
|
||||||
def test_dynamic_construction(self):
|
def test_dynamic_construction(self):
|
||||||
# It's possible to construct a Form dynamically by adding to the self.fields
|
# It's possible to construct a Form dynamically by adding to the self.fields
|
||||||
# dictionary in __init__(). Don't forget to call Form.__init__() within the
|
# dictionary in __init__(). Don't forget to call Form.__init__() within the
|
||||||
|
|
Loading…
Reference in New Issue