#24988: ValidationError fails to set code
---------------------------------+--------------------------------------
Reporter: michaeljohnbarr | Owner: nobody
Type: Bug | Status: new
Component: Core (Other) | Version: 1.8
Severity: Normal | Resolution:
Keywords: ValidationError | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
---------------------------------+--------------------------------------
Changes (by michaeljohnbarr):
* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0
Old description:
> Assuming the following model:
>
> {{{
> class DateRange(models.Model):
> start = models.DateField()
> end = models.DateField
>
> def clean(self):
> super(DateRange, self).clean()
>
> if self.start and self.end:
> if self.start >= self.end:
> raise ValidationError(
> message={
> 'start': _(
> '{start} must come before {end}.'.format(
> start=self._meta.get_field(
> 'start'
> ).verbose_name,
> end=self._meta.get_field(
> 'end'
> ).verbose_name,
> )
> ),
> 'end': _(
> '{end} must come after {start}.'.format(
> end=self._meta.get_field(
> 'end'
> ).verbose_name,
> start=self._meta.get_field(
> 'start'
> ).verbose_name,
> )
> ),
> },
> code='invalid',
> )
> }}}
>
> After then validating the model with a ModelForm, the `code` fails to be
> set to `'invalid'` but is instead an empty string. I discovered this when
> I was programming tests:
>
> {{{
> def test_start_before_end_validation(self):
> """An employee is not allowed to be their own manager."""
> TestForm = modelform_factory(
> model=DateRange,
> fields=('start', 'end')
> )
>
> form = TestForm(
> data=dict(
> start=date(2015, 1, 1),
> end=date(1900, 12, 31) # WHOOPS!
> )
> )
>
> self.assertEqual(first=form.is_valid(), second=False)
>
> # The below fails because the code is '' - we have to leave off
> the code
> self.assertTrue(form.has_error(field='start'), code='invalid')
> self.assertTrue(form.has_error(field='end'), code='invalid')
> }}}
>
> Upon inspection of the the `ValidationError` code, I discovered that that
> any `ValidationError` that is raised with a type of `dict` or a `list` as
> the `message` will never set the code:
>
> {{{
> 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)
> if hasattr(message, 'error_dict'):
> self.error_list.extend(sum(message.error_dict.values(), []))
> else:
> self.error_list.extend(message.error_list)
> }}}
>
> According to the
> [https://docs.djangoproject.com/en/1.8/ref/forms/validation/#raising-
> validationerror documentation on raising ValidationError], it is
> suggested to "Provide a descriptive error code to the constructor." Is
> this a bug?
>
> I believe the "fix" would be as simple as to pass `code=code` following
> the message/messages, but I may be wrong. Thoughts?
New description:
Assuming the following model:
{{{
class DateRange(models.Model):
start = models.DateField()
end = models.DateField
def clean(self):
super(DateRange, self).clean()
if self.start and self.end:
if self.start >= self.end:
raise ValidationError(
message={
'start': _(
'{start} must come before {end}.'.format(
start=self._meta.get_field(
'start'
).verbose_name,
end=self._meta.get_field(
'end'
).verbose_name,
)
),
'end': _(
'{end} must come after {start}.'.format(
end=self._meta.get_field(
'end'
).verbose_name,
start=self._meta.get_field(
'start'
).verbose_name,
)
),
},
code='invalid',
)
}}}
After then validating the model with a ModelForm, the `code` fails to be
set to `'invalid'` but is instead an empty string. I discovered this when
I was programming tests:
{{{
def test_start_before_end_validation(self):
"""Start date must come before end date."""
TestForm = modelform_factory(
model=DateRange,
fields=('start', 'end')
)
form = TestForm(
data=dict(
start=date(2015, 1, 1),
end=date(1900, 12, 31) # WHOOPS!
)
)
self.assertEqual(first=form.is_valid(), second=False)
# The below fails because the code is '' - we have to leave off
the code
self.assertTrue(form.has_error(field='start'), code='invalid')
self.assertTrue(form.has_error(field='end'), code='invalid')
}}}
Upon inspection of the the `ValidationError` code, I discovered that that
any `ValidationError` that is raised with a type of `dict` or a `list` as
the `message` will never set the code:
{{{
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)
if hasattr(message, 'error_dict'):
self.error_list.extend(sum(message.error_dict.values(), []))
else:
self.error_list.extend(message.error_list)
}}}
According to the
[https://docs.djangoproject.com/en/1.8/ref/forms/validation/#raising-
validationerror documentation on raising ValidationError], it is suggested
to "Provide a descriptive error code to the constructor." Is this a bug?
I believe the "fix" would be as simple as to pass `code=code` following
the message/messages, but I may be wrong. Thoughts?
--
--
Ticket URL: <https://code.djangoproject.com/ticket/24988#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/073.bf1b4bebe3c38dc4bd8c32c3679f6019%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.