Code

Opened 4 years ago

Closed 4 years ago

Last modified 3 years ago

#12858 closed (fixed)

Callable default on DateField + custom date format = widget._has_changed always true, forms fail to validate

Reported by: camillo Owned by: nobody
Component: Forms Version: master
Severity: Keywords: dateinput format
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: no
Easy pickings: UI/UX:

Description (last modified by russellm)

I have a ModelFormset with some extra forms. The model the forms are based on contains a DateField with default=datetime.date.today, and other fields which are required. It must be possible to submit the formset without filling in all extra forms (which wouldn't validate when empty, due to the fact that there are required fields), so the formset sets empty_permitted on the extra forms: this lets those forms skip validation as long as their fields have not changed (if self.empty_permitted and not self.has_changed()) in BaseForm.full_clean. So far this is all standard formset behaviour.

I need to render the dates in a custom (ie local) format, so I have an __init__ method on my modelform which replaces the widgets of DateFields with a DateInput created with the desired format. This is also rather standard widget customization.

However, this causes Django to incorrectly treat every instance of the form as changed. The problem stems from the fact that Django compares the current data and the initial data in string form. Since the model DateField has a callable default, it generates a form field with show_hidden_initial set to True; this causes the form to store the initial value in a separate hidden widget, created by calling hidden_widget on the field. This corresponds to the HiddenInput class for most formfields, including DateField. When HiddenInput renders, it formats the date using the default ISO format instead of the custom format used by the corresponding DateInput. Therefore, DateInput._has_changed sees two different strings (eg 2010-02-13 vs 13/02/2010), and thinks the field has changed even when it has not. In some cases, it may also think it has not changed when it has: for example, when one format is D/M/Y and the other is M/D/Y, and the user happens to change the date from Feb. 1 to Jan. 2 (01/02/2010 vs 01/02/2010).

The patch fixes this problem by having DateInput._has_changed attempt to parse the initial value using the same format used by the HiddenInput. If the initial value was a string, and was in that format, then it is converted to a date object, which is later converted back into a string using the DateInput's own format; otherwise, everything remains as before.

Attachments (2)

django_datefield_has_changed_fix.diff (1.2 KB) - added by camillo 4 years ago.
patch fixing the bug
12698-backport.diff (7.1 KB) - added by jkocherhans 4 years ago.
I don't think this is the correct way to backport this, but the real way might be too hard to be worth it.

Download all attachments as: .zip

Change History (8)

Changed 4 years ago by camillo

patch fixing the bug

comment:1 Changed 4 years ago by russellm

  • Needs documentation unset
  • Needs tests set
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

comment:2 Changed 4 years ago by russellm

  • Description modified (diff)

comment:3 Changed 4 years ago by jkocherhans

  • Resolution set to fixed
  • Status changed from new to closed

(In [12698]) Fixed #12858. DateTime related widgets now handle custom formats properly in _has_changed. Thanks for the initial patch, camillo.

Changed 4 years ago by jkocherhans

I don't think this is the correct way to backport this, but the real way might be too hard to be worth it.

comment:4 Changed 4 years ago by jkocherhans

(In [12699]) [1.1.X] Fixed #12858. DateTime related widgets now handle custom formats properly in _has_changed. Backport of r12698 from trunk.

comment:5 Changed 4 years ago by jkocherhans

Actually, nevermind. 12698-backport.diff probably is the right fix because I don't think force_unicode on a datetime, date, or, time will use a locale-specific format. If I'm wrong about that, the fix applied to 1.1.X is incorrect.

comment:6 Changed 3 years ago by jacob

  • milestone 1.2 deleted

Milestone 1.2 deleted

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.