#34385 closed Bug (wontfix)
BaseTemporalField child fields causing AttributeError to be raised by calling form.is_valid().
Reported by: | Fábio David Freitas | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 4.1 |
Severity: | Normal | Keywords: | DateField DateTimeField validation AttributeError |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When validating a form with a temporal field that has been passed a non str
native value, AttributeError
is raised.
The problem happens in DateField
, DateTimeField
, and TimeField
fields. The respective temporal field's to_python
method treats the input assuming it is of type str
, datetime.datetime
, datetime.date
, or datetime.timedelta
. So when something like an integer is fed to a field and clean
is called in the validation process, AttributeError
is raised instead of a ValidationError
, causing the form not to catch the error since it only catches ValidationError
instances, and, therefore, raising an unexpected exception from a, in my opinion, validation error.
Reproducing the problem
You can test this through the form validation:
from django import forms class MyForm(forms.Form): datefield = forms.DateField() form = MyForm({'datefield': 1}) form.is_valid() ------------------------------------------------------------------------------------ > def to_python(self, value): > value = value.strip() E AttributeError: 'int' object has no attribute 'strip'
Or directly with a field:
from django.forms import DateField field = DateField() field.to_python(1) ------------------------------------------------------------------------------------ > def to_python(self, value): > value = value.strip() E AttributeError: 'int' object has no attribute 'strip'
Note that through the form validation process the problem is more apparent, since when calling is_valid
, we do not expect an exception raised from said validation, we expect the form to store its validation errors so we can handle them directly. Passing an integer to a DateField
, however, causes an exception to be raised when validating a form.
Test case
from django import forms from unittest import TestCase class TemporalFieldValidationTests(TestCase): def test_date_field_raises_validationerror_when_value_is_not_str_or_temporal_object(self): date_field = forms.DateField() with self.assertRaises(forms.ValidationError): date_field.clean(1) def test_time_field_raises_validationerror_when_value_is_not_str_or_temporal_object(self): time_field = forms.TimeField with self.assertRaises(forms.ValidationError): time_field.clean(1) def test_datetime_field_raises_validationerror_when_value_is_not_str_or_temporal_object(self): datetime_field = forms.DateTimeField() with self.assertRaises(forms.ValidationError): datetime_field.clean(1)
Considerations
- Tested on versions
3.2.16
, and4.1.7
- I will open a PR to solve this
Change History (2)
follow-up: 2 comment:1 by , 22 months ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
comment:2 by , 22 months ago
Replying to Mariusz Felisiak:
Thanks for this ticket, however I don't see anything wrong in this behavior,
to_python()
accepts the raw value from the widget i.e. string, and it's not designed to accept anything and never crash. Can you explain how you ended up with passingint
to this method?
Thanks for the response, that makes sense. What I did in my code to get this behavior is basically the first code section of Reproducing the problem
from the ticket, which is creating a form with a DateField
and populating it with data when instantiating the form. I am only using the form for validation, but the actual input comes from a JSON request.
From your response I am getting the impression that django forms are only supposed to be used when it's gonna be populated by the form itself on the template. Is that the case? I was under the impression that forms could be used as a "Struct with validation and streamlined model access".
Thanks for this ticket, however I don't see anything wrong in this behavior,
to_python()
accepts the raw value from the widget i.e. string, and it's not designed to accept anything and never crash. Can you explain how you ended up with passingint
to this method?