Opened 14 months ago

Closed 14 months ago

Last modified 14 months ago

#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, and 4.1.7
  • I will open a PR to solve this

Change History (2)

comment:1 by Mariusz Felisiak, 14 months ago

Resolution: wontfix
Status: newclosed

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 passing int to this method?

in reply to:  1 comment:2 by Fábio David Freitas, 14 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 passing int 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".

Note: See TracTickets for help on using tickets.
Back to Top