Code

Opened 7 years ago

Closed 3 years ago

Last modified 2 years ago

#3672 closed Uncategorized (fixed)

newforms: DateField doesn't handle date output formats

Reported by: orestis@… Owned by: ajs
Component: Forms Version: master
Severity: Normal Keywords: DateField l10n localization format input output
Cc: scott@…, gary.wilson@…, jesse.lovelace@…, ajs@…, blake@…, andy@…, tao_jonesin@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

As it stands, the when using a newforms.DateField with specified input
formats, it gets parsed alright, but when rendering the form there is
no way to specify the output format, it gets printed as YYYY-MM-DD.

By looking in the source code, I haven't been able to find anything related to this, so I think DateField needs some tweaking.

This not only is a bad localization practice, it makes _using_ the
input formats impossible, since when re-submitting a populated form
without changes, you get validation errors.

Since we can define multiple input formats, but there is only one output format (obviously), I suggest:

a) Defining a new parameter, output_format
b) Make it optional, and set it to the first input format defined in the form field.

Attachments (6)

date-time-formatting-widgets.py (1.1 KB) - added by scott@… 7 years ago.
Widgets that format date or time before rendering
date-time-formatting-widgets.2.py (1.3 KB) - added by scott@… 7 years ago.
Bug fix: If an invalid date or time is entered the widgets don't try to format it
date-time-widgets-tests.diff (4.4 KB) - added by scott@… 7 years ago.
Widgets (slightly remodeled) and tests as a diff
date-time-widgets-tests-2.diff (4.4 KB) - added by ajs 6 years ago.
updated to work with trunk and use translated DATE_FORMAT and TIME_FORMAT as well
date-time-widgets-tests-7601.diff (2.2 KB) - added by ajs 6 years ago.
update the patch again, removed the usage for DATE_FORMAT from the translation.
ticket#3672--patch-test.diff (5.3 KB) - added by Dinoboff 6 years ago.
Patch and test

Download all attachments as: .zip

Change History (29)

comment:1 Changed 7 years ago by Simon G. <dev@…>

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Design decision needed

comment:2 Changed 7 years ago by scott@…

  • Cc scott@… added

I ran in to the same problem today. Seems like maybe this is a job for the widget, rather than the field? My interim solution was to write widgets that derive from TextInput, override render and format the value before it is written out. Code for the widgets is attached.

Usage:

class MyForm(forms.Form):
    date = forms.DateField(widget=DateFormattedTextInput())
    time = forms.TimeField(widget=TimeFormattedTextInput())
    # or pass output format to widget:
    date = forms.DateField(widget=DateFormattedTextInput(format='j M Y'))

Changed 7 years ago by scott@…

Widgets that format date or time before rendering

comment:3 Changed 7 years ago by Ville Säävuori <Ville@…>

I ran into this today.

For me it would be enough to make the field render its content according to input_formats parameter.

But as said, this bug indeed leaves the DateField unusable for now :(

comment:4 Changed 7 years ago by Ville Säävuori <Ville@…>

Whipped up a quick workaround:

First, make timeformat variable

from datetime import date
TIME_FORMAT = '%d.%m.%Y'

then use it in input_formats

date_of_birth = forms.DateField(input_formats=[TIME_FORMAT], label="Date of Birth", required=False, help_text="Format like 17.12.1979")

and when initializing the form, use strftime

date_of_birth = user.get_profile().date_of_birth
form = MyForm(initial={'date_of_birth': date_of_birth.strftime(TIME_FORMAT)})

Not very DRY or pretty, but it seems to work.

comment:5 Changed 7 years ago by Gary Wilson <gary.wilson@…>

  • Cc gary.wilson@… added

Changed 7 years ago by scott@…

Bug fix: If an invalid date or time is entered the widgets don't try to format it

comment:6 Changed 7 years ago by anonymous

  • Cc jesse.lovelace@… added

comment:7 Changed 7 years ago by ubernostrum

See also #3533.

comment:8 Changed 7 years ago by gwilson

  • Has patch set
  • Needs tests set
  • Patch needs improvement set
  • Triage Stage changed from Design decision needed to Accepted

I'm leaning towards the patch attached to this ticket rather than the one on #3533 since the one here splits the Date and Time widgets and makes use of formats in settings.py.

How about some tests and a patch in diff format.

Changed 7 years ago by scott@…

Widgets (slightly remodeled) and tests as a diff

comment:9 Changed 7 years ago by scott@…

Note that these widgets take a Django style format string like 'N j, Y' versus Python strftime() style like '%b %d, %Y'.

It means you could have a field definition with two different styles of date format strings like:

class MyForm(forms.Form):
    start_date  = forms.DateField(widget=DateTextInput(format='d/m/y'), input_formats=('%d/%m/%y',))

However, I think it makes sense to use Django format strings for output since it is used elsewhere in the UI and is perhaps more powerful. DateField uses strftime() patterns for input, but it is parsing using strptime() so that's the natural format.

comment:10 Changed 6 years ago by David Jones <davesgonebananas@…>

Found this incredibly useful. Have implemented a modified version that uses datetime.strftime and it is working perfectly for me. However, I'm surprised this has been open for 1 year and has not made yet it into trunk.

Changed 6 years ago by ajs

updated to work with trunk and use translated DATE_FORMAT and TIME_FORMAT as well

comment:11 Changed 6 years ago by ajs

  • Cc ajs@… added

new patch now works with trunk.

I also changed how the date and time formats are determined.

  1. user supplied
  2. format from translation using ugettext
  3. format from settings.

I also tried to add the new text inputs as default widgets to the DateField and TimeField model fields.
This breaks forms tests and modelforms tests, since the tests supply unicode strings instead of datetime.date or datetime objects to the date and time widgets. The new widgets return None if they are not supplied with a datetime.date or datetime.datetime instances.

Now my question is: Does a widgets have to handle unicode strings (thinking about POST or GET values) or do the widgets always get value from to_python?
Actually thinking about it some more having to handle unicode is probably a must since I can use a Form without a Model.

So I'd leave unicode strings as they are and only format date and datetime instance. Is that OK?

Changed 6 years ago by ajs

update the patch again, removed the usage for DATE_FORMAT from the translation.

comment:12 Changed 6 years ago by ajs

  • Owner changed from nobody to ajs

the usage of DATE_FORMAT from the translation or utils.translation.get_date_format doesn't actually make much sense for input control. The german DATE_FORMAT is 12. Apr 2008 which, when displayed in an input control doesn't feel correct.

comment:13 Changed 6 years ago by anonymous

  • Cc blake@… added

comment:14 Changed 6 years ago by Dinoboff

Thanks for the patches. I am new to Python and Django can't really understand how all that works.

Here the solution for DateField that I am using; it doesn't rely on settings.DATE_FORMAT but on the field input_formats property:

from django import newforms as forms
from django.newforms.fields import DEFAULT_DATE_INPUT_FORMATS
import datetime

DEFAULT_DATE_OUTPUT_FORMATS = DEFAULT_DATE_INPUT_FORMATS[0]

class FormattedTextInput(forms.widgets.TextInput):
    "Overrides TextInput to render formatted value."
    def render(self, name, value, attrs=None):
        formatted_value = None
        if value:
            formatted_value = self.format_value(value)
        return super(FormattedTextInput, self).render(name, formatted_value, attrs)

class DateFormattedTextInput(FormattedTextInput):
    "Renders formatted date."
    def __init__(self, format=None, attrs=None):
        super(DateFormattedTextInput, self).__init__(attrs)
        self.format = format or DEFAULT_DATE_OUTPUT_FORMATS

    def format_value(self, value):
        if isinstance(value, datetime.date) or isinstance(value, datetime.datetime):
            return value.strftime(self.format)
        else:
            return value

class DateField(forms.DateField):
    widget = DateFormattedTextInput

    def __init__(self, *args, **kwargs):
        super(DateField, self).__init__(*args, **kwargs)
        self.widget.format = self.input_formats[0]

Hope it helps.

Changed 6 years ago by Dinoboff

Patch and test

comment:15 Changed 6 years ago by anonymous

  • Needs tests unset
  • Patch needs improvement unset

DateTimeField's widget (DateTimeInput) already allows to format date and datetime object.

comment:16 Changed 5 years ago by guettli

  • Cc hv@… added

comment:17 follow-up: Changed 5 years ago by marxis.rene@…

  • milestone set to post-1.0

Hello

i think

def format_value(self, value):

if isinstance(value, datetime.date) or isinstance(value, datetime.datetime):

return value.strftime(self.format)

else:

return value

needs to be changed to:

def format_value(self, value):

if isinstance(value, datetime.date) or isinstance(value, datetime.datetime):

try:

return value.strftime(self.format)

except:

return value

else:

return value

Otherwise the value.strftime(self.format) would crash the form (form render returns "") if the field-value does not validate on calling form.is_valid()

If my approach is wrong please let me know. I'm very new to django/python :)

comment:18 Changed 5 years ago by anonymous

  • milestone post-1.0 deleted

Milestone post-1.0 deleted

comment:19 Changed 5 years ago by anonymous

  • Cc andy@… added

comment:20 Changed 5 years ago by taojonesin

  • Cc tao_jonesin@… added

comment:21 in reply to: ↑ 17 Changed 4 years ago by adamnelson

  • Patch needs improvement set

No response to marxis.rene@googlemail.com yet so marking as patch needs improvement until somebody responds to that issue. Comment 15 marks this as not even relevant anymore - not sure either way.

comment:22 Changed 3 years ago by ramiro

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

I think these needs were fulfilled with the changes introduced in r11964 (GSoC 2010 project) and further refinements committed later since then.

comment:23 Changed 2 years ago by guettli

  • Cc hv@… removed
  • Easy pickings unset
  • Severity set to Normal
  • Type set to Uncategorized
  • UI/UX unset

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.