Opened 5 years ago

Closed 5 years ago

Last modified 4 years ago

#21173 closed Bug (fixed)

Django DateTimeInput determines language/locale at startup time but this may change later, resulting in validation errors

Reported by: django-locale-issue@… Owned by: Claude Paroz
Component: Forms Version: master
Severity: Normal Keywords: forms, widgets, locale
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


My application works in two locales, 'nl' and 'en'. In some situations, when editing content, Django formats datetimes in the wrong format which won't validate later.

The problem, I suspect is in the fact that DateTimeInput determines/stores the locale format in its init, which means at startup/import time in most cases. It cannot handle a changing locale later.

This is with Django 1.4.x but the relevant code doesn't appear to be any different on 1.5.x

I've tested/verified the issue in an interactive django shell session:

# In my settings I have LANGUAGE_CODE = 'nl' 
# but the user can switch to english later.
# Forms, fields and widgets are usually initialized at startup/import time
# DateTimeField users DateTimeInput which uses the current language to determine the preferred format

In [1]: from django.contrib.auth.forms import UserChangeForm

In [2]: print UserChangeForm().fields['last_login'].widget.format
%d-%m-%Y %H:%M:%S

# this is a rather dutch format
# imagine the user switches to english
In [3]: from django.utils import translation

In [4]: translation.activate('en')

In [5]: print UserChangeForm().fields['last_login'].widget.format
%d-%m-%Y %H:%M:%S

# the format remains unchanged, which isn't good but not fatal. Let's format a string using this
In [6]: from datetime import datetime

In [7]: now =

In [8]: print UserChangeForm().fields['last_login'].widget._format_value(now)
26-09-2013 15:33:13

# now feed this string back to the field, as if the form was filled in and then submitted without change
In [11]: print UserChangeForm().fields['last_login'].to_python("26-09-2013 15:33:13")
ValidationError                           Traceback (most recent call last)
/home/ivo/.buildout/eggs/Django-1.4.5-py2.7.egg/django/core/management/commands/shell.pyc in <module>()
----> 1 print UserChangeForm().fields['last_login'].to_python("26-09-2013 15:33:13")

/home/ivo/.buildout/eggs/Django-1.4.5-py2.7.egg/django/forms/fields.pyc in to_python(self, value)
    435                 return None
    436             value = '%s %s' % tuple(value)
--> 437         result = super(DateTimeField, self).to_python(value)
    438         return from_current_timezone(result)

/home/ivo/.buildout/eggs/Django-1.4.5-py2.7.egg/django/forms/fields.pyc in to_python(self, value)
    354                         except ValueError:
    355                             continue
--> 356         raise ValidationError(self.error_messages['invalid'])
    358     def strptime(self, value, format):

ValidationError: [u'Enter a valid date/time.']

# oops!

Change History (9)

comment:1 Changed 5 years ago by Daniele Procida

Triage Stage: UnreviewedAccepted

comment:2 Changed 5 years ago by Simon Charette

AFAIK determining locale at the form initialization phase shouldn't cause any issues because LocaleMiddleware already activated the current language.

How are you activating user specific language in your application?

comment:3 Changed 5 years ago by django-locale-issue@…

Form initialization kicks in at startup / import time, LocaleMiddleWare doesn't happen until the first request.
LocaleMiddleware may activate a different language than the widget was initialized with.

comment:4 Changed 5 years ago by Claude Paroz

I think the basic issue is that widget.is_localized is False by default, and contrib.auth forms doesn't set it. In your own forms, you can probably activate is_localized and the self.format variable set in the __init__ method will not be used.

Personally, I'd vote for setting is_localized to the settings.USE_L10N value by default. But this might be backwards incompatible.

A more contained fix would be to let self.format to None when it is not specified in the widget constructor, and then getting the format only if and when it is needed in _format_value.

comment:5 Changed 5 years ago by Claude Paroz

Owner: changed from nobody to Claude Paroz
Status: newassigned
Version: 1.4master

comment:6 Changed 5 years ago by Claude Paroz

Has patch: set

Pull request:
Isn't less code better :-)

comment:7 Changed 5 years ago by Claude Paroz <claude@…>

Resolution: fixed
Status: assignedclosed

In 00a73c1c699e3df2790caf56635647bd72e3cd21:

Fixed #21173 -- Stopped fixing format for date-based widgets at init time

Thanks Marc Tamlyn for the review.

comment:8 Changed 4 years ago by Markus Bertheau

Any chance this makes it into 1.6? It's a pretty serious issue where the user accessing the page with a different language preference breaks date(time) fields in all forms.

comment:9 Changed 4 years ago by Markus Bertheau

I noticed that now date and time widgets ignore is_localized and always localize. I like this, but I haven't seen it discussed, so I wondered whether this was an oversight. Previous behaviour was to use the first format for the LANGUAGE_CODE in as a default format that'll change only when overridden manually or when is_localized is True.

I think it makes sense this way around. If you need a certain format instead of a localized one, it should be controlled by passing format to the widget, not by settings.LANGUAGE_CODE.

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