Fixed #12444 - Date based widgets now correctly handle input values when using locale-aware formatting. Also fixes #7656.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12029 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
6eb02fa9bb
commit
bf33d3eb1d
|
@ -41,21 +41,21 @@ class FilteredSelectMultiple(forms.SelectMultiple):
|
|||
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
|
||||
return mark_safe(u''.join(output))
|
||||
|
||||
class AdminDateWidget(forms.TextInput):
|
||||
class AdminDateWidget(forms.DateTimeInput):
|
||||
class Media:
|
||||
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
|
||||
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
|
||||
|
||||
def __init__(self, attrs={}):
|
||||
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
|
||||
def __init__(self, attrs={}, format=None):
|
||||
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}, format=format)
|
||||
|
||||
class AdminTimeWidget(forms.TextInput):
|
||||
class AdminTimeWidget(forms.TimeInput):
|
||||
class Media:
|
||||
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
|
||||
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
|
||||
|
||||
def __init__(self, attrs={}):
|
||||
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
|
||||
def __init__(self, attrs={}, format=None):
|
||||
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}, format=format)
|
||||
|
||||
class AdminSplitDateTime(forms.SplitDateTimeWidget):
|
||||
"""
|
||||
|
|
|
@ -10,8 +10,7 @@ from django.utils.html import escape, conditional_escape
|
|||
from django.utils.translation import ugettext
|
||||
from django.utils.encoding import StrAndUnicode, force_unicode
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.formats import localize
|
||||
from django.utils import datetime_safe
|
||||
from django.utils import datetime_safe, formats
|
||||
from datetime import time
|
||||
from util import flatatt
|
||||
from urlparse import urljoin
|
||||
|
@ -209,7 +208,7 @@ class Input(Widget):
|
|||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||
if value != '':
|
||||
# Only add the 'value' attribute if a value is non-empty.
|
||||
final_attrs['value'] = force_unicode(localize(value, is_input=True))
|
||||
final_attrs['value'] = force_unicode(formats.localize_input(value))
|
||||
return mark_safe(u'<input%s />' % flatatt(final_attrs))
|
||||
|
||||
class TextInput(Input):
|
||||
|
@ -284,7 +283,7 @@ class Textarea(Widget):
|
|||
|
||||
class DateInput(Input):
|
||||
input_type = 'text'
|
||||
format = '%Y-%m-%d' # '2006-10-25'
|
||||
format = None
|
||||
|
||||
def __init__(self, attrs=None, format=None):
|
||||
super(DateInput, self).__init__(attrs)
|
||||
|
@ -295,8 +294,7 @@ class DateInput(Input):
|
|||
if value is None:
|
||||
return ''
|
||||
elif hasattr(value, 'strftime'):
|
||||
value = datetime_safe.new_date(value)
|
||||
return value.strftime(self.format)
|
||||
return formats.localize_input(value, self.format)
|
||||
return value
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
|
@ -308,7 +306,7 @@ class DateInput(Input):
|
|||
|
||||
class DateTimeInput(Input):
|
||||
input_type = 'text'
|
||||
format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59'
|
||||
format = None
|
||||
|
||||
def __init__(self, attrs=None, format=None):
|
||||
super(DateTimeInput, self).__init__(attrs)
|
||||
|
@ -319,8 +317,7 @@ class DateTimeInput(Input):
|
|||
if value is None:
|
||||
return ''
|
||||
elif hasattr(value, 'strftime'):
|
||||
value = datetime_safe.new_datetime(value)
|
||||
return value.strftime(self.format)
|
||||
return formats.localize_input(value, self.format)
|
||||
return value
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
|
@ -332,7 +329,7 @@ class DateTimeInput(Input):
|
|||
|
||||
class TimeInput(Input):
|
||||
input_type = 'text'
|
||||
format = '%H:%M:%S' # '14:30:59'
|
||||
format = None
|
||||
|
||||
def __init__(self, attrs=None, format=None):
|
||||
super(TimeInput, self).__init__(attrs)
|
||||
|
@ -343,7 +340,7 @@ class TimeInput(Input):
|
|||
if value is None:
|
||||
return ''
|
||||
elif hasattr(value, 'strftime'):
|
||||
return value.strftime(self.format)
|
||||
return formats.localize_input(value, self.format)
|
||||
return value
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
|
|
|
@ -4,8 +4,8 @@ import datetime
|
|||
from django.conf import settings
|
||||
from django.utils.translation import get_language, to_locale, check_for_language
|
||||
from django.utils.importlib import import_module
|
||||
from django.utils import dateformat
|
||||
from django.utils import numberformat
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils import dateformat, numberformat, datetime_safe
|
||||
|
||||
def get_format_modules():
|
||||
"""
|
||||
|
@ -65,11 +65,10 @@ def number_format(value, decimal_pos=None):
|
|||
get_format('THOUSAND_SEPARATOR'),
|
||||
)
|
||||
|
||||
def localize(value, is_input=False):
|
||||
def localize(value):
|
||||
"""
|
||||
Checks value, and if it has a localizable type (date,
|
||||
number...) it returns the value as a string using
|
||||
current locale format
|
||||
Checks if value is a localizable type (date, number...) and returns it
|
||||
formatted as a string using current locale format
|
||||
"""
|
||||
if settings.USE_L10N:
|
||||
if isinstance(value, decimal.Decimal):
|
||||
|
@ -79,19 +78,27 @@ def localize(value, is_input=False):
|
|||
elif isinstance(value, int):
|
||||
return number_format(value)
|
||||
elif isinstance(value, datetime.datetime):
|
||||
if not is_input:
|
||||
return date_format(value, 'DATETIME_FORMAT')
|
||||
else:
|
||||
return value.strftime(get_format('DATETIME_INPUT_FORMATS')[0])
|
||||
elif isinstance(value, datetime.date):
|
||||
if not is_input:
|
||||
return date_format(value)
|
||||
else:
|
||||
return value.strftime(get_format('DATE_INPUT_FORMATS')[0])
|
||||
elif isinstance(value, datetime.time):
|
||||
if not is_input:
|
||||
return date_format(value, 'TIME_FORMAT')
|
||||
else:
|
||||
return value.strftime(get_format('TIME_INPUT_FORMATS')[0])
|
||||
return value
|
||||
|
||||
def localize_input(value, default=None):
|
||||
"""
|
||||
Checks if an input value is a localizable type and returns it
|
||||
formatted with the appropriate formatting string of the current locale.
|
||||
"""
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = datetime_safe.new_datetime(value)
|
||||
format = smart_str(default or get_format('DATETIME_INPUT_FORMATS')[0])
|
||||
return value.strftime(format)
|
||||
elif isinstance(value, datetime.date):
|
||||
value = datetime_safe.new_date(value)
|
||||
format = smart_str(default or get_format('DATE_INPUT_FORMATS')[0])
|
||||
return value.strftime(format)
|
||||
elif isinstance(value, datetime.time):
|
||||
format = smart_str(default or get_format('TIME_INPUT_FORMATS')[0])
|
||||
return value.strftime(format)
|
||||
return value
|
||||
|
|
|
@ -77,6 +77,9 @@ __test__ = {'WIDGETS_TESTS': """
|
|||
>>> from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime
|
||||
>>> from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget, ManyToManyRawIdWidget
|
||||
>>> from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
||||
>>> from django.utils.translation import activate, deactivate
|
||||
>>> from django.conf import settings
|
||||
|
||||
|
||||
Calling conditional_escape on the output of widget.render will simulate what
|
||||
happens in the template. This is easier than setting up a template and context
|
||||
|
@ -94,6 +97,12 @@ HTML escaped.
|
|||
>>> w = AdminSplitDateTime()
|
||||
>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
|
||||
<p class="datetime">Date: <input value="2007-12-01" type="text" class="vDateField" name="test_0" size="10" /><br />Time: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>
|
||||
>>> activate('de-at')
|
||||
>>> settings.USE_L10N = True
|
||||
>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
|
||||
<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>
|
||||
>>> deactivate()
|
||||
>>> settings.USE_L10N = False
|
||||
|
||||
>>> band = Band.objects.create(pk=1, name='Linkin Park')
|
||||
>>> album = band.album_set.create(name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg')
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.contrib import admin
|
|||
from django.contrib.admin import widgets
|
||||
from unittest import TestCase
|
||||
from django.test import TestCase as DjangoTestCase
|
||||
from django.db.models import DateField
|
||||
import models
|
||||
|
||||
class AdminFormfieldForDBFieldTests(TestCase):
|
||||
|
@ -89,7 +90,7 @@ class AdminFormfieldForDBFieldTests(TestCase):
|
|||
|
||||
def testFormfieldOverrides(self):
|
||||
self.assertFormfield(models.Event, 'start_date', forms.TextInput,
|
||||
formfield_overrides={'widget': forms.TextInput})
|
||||
formfield_overrides={DateField: {'widget': forms.TextInput}})
|
||||
|
||||
def testFieldWithChoices(self):
|
||||
self.assertFormfield(models.Member, 'gender', forms.Select)
|
||||
|
|
|
@ -10,6 +10,8 @@ tests = r"""
|
|||
... from decimal import Decimal
|
||||
... except ImportError:
|
||||
... from django.utils._decimal import Decimal
|
||||
>>> from django.utils.translation import activate, deactivate
|
||||
>>> from django.conf import settings
|
||||
|
||||
###########
|
||||
# Widgets #
|
||||
|
@ -1082,6 +1084,13 @@ True
|
|||
False
|
||||
>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06/05/2008', u'12:41'])
|
||||
True
|
||||
>>> activate('de-at')
|
||||
>>> settings.USE_L10N = True
|
||||
>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06.05.2008', u'12:41'])
|
||||
True
|
||||
>>> deactivate()
|
||||
>>> settings.USE_L10N = False
|
||||
|
||||
|
||||
# DateTimeInput ###############################################################
|
||||
|
||||
|
@ -1099,6 +1108,12 @@ u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
|
|||
u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
|
||||
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
|
||||
u'<input type="text" name="date" value="2007-09-17 12:51:00" />'
|
||||
>>> activate('de-at')
|
||||
>>> settings.USE_L10N = True
|
||||
>>> w.render('date', d)
|
||||
u'<input type="text" name="date" value="17.09.2007 12:51:34" />'
|
||||
>>> deactivate()
|
||||
>>> settings.USE_L10N = False
|
||||
|
||||
Use 'format' to change the way a value is displayed.
|
||||
>>> w = DateTimeInput(format='%d/%m/%Y %H:%M')
|
||||
|
@ -1107,6 +1122,7 @@ u'<input type="text" name="date" value="17/09/2007 12:51" />'
|
|||
>>> w._has_changed(d, '17/09/2007 12:51')
|
||||
False
|
||||
|
||||
|
||||
# DateInput ###################################################################
|
||||
|
||||
>>> w = DateInput()
|
||||
|
@ -1125,6 +1141,13 @@ We should be able to initialize from a unicode value.
|
|||
>>> w.render('date', u'2007-09-17')
|
||||
u'<input type="text" name="date" value="2007-09-17" />'
|
||||
|
||||
>>> activate('de-at')
|
||||
>>> settings.USE_L10N = True
|
||||
>>> w.render('date', d)
|
||||
u'<input type="text" name="date" value="17.09.2007" />'
|
||||
>>> deactivate()
|
||||
>>> settings.USE_L10N = False
|
||||
|
||||
Use 'format' to change the way a value is displayed.
|
||||
>>> w = DateInput(format='%d/%m/%Y')
|
||||
>>> w.render('date', d)
|
||||
|
@ -1153,6 +1176,13 @@ We should be able to initialize from a unicode value.
|
|||
>>> w.render('time', u'13:12:11')
|
||||
u'<input type="text" name="time" value="13:12:11" />'
|
||||
|
||||
>>> activate('de-at')
|
||||
>>> settings.USE_L10N = True
|
||||
>>> w.render('date', d)
|
||||
u'<input type="text" name="date" value="17.09.2007" />'
|
||||
>>> deactivate()
|
||||
>>> settings.USE_L10N = False
|
||||
|
||||
Use 'format' to change the way a value is displayed.
|
||||
>>> w = TimeInput(format='%H:%M')
|
||||
>>> w.render('time', t)
|
||||
|
@ -1176,6 +1206,12 @@ u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" n
|
|||
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />'
|
||||
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
|
||||
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:00" />'
|
||||
>>> activate('de-at')
|
||||
>>> settings.USE_L10N = True
|
||||
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
|
||||
u'<input type="hidden" name="date_0" value="17.09.2007" /><input type="hidden" name="date_1" value="12:51:00" />'
|
||||
>>> deactivate()
|
||||
>>> settings.USE_L10N = False
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django import template, forms
|
||||
from django.forms.extras import SelectDateWidget
|
||||
from models import Company
|
||||
|
||||
class I18nForm(forms.Form):
|
||||
decimal_field = forms.DecimalField()
|
||||
|
@ -11,3 +12,6 @@ class I18nForm(forms.Form):
|
|||
class SelectDateForm(forms.Form):
|
||||
date_field = forms.DateField(widget=SelectDateWidget)
|
||||
|
||||
class CompanyForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Company
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
from datetime import datetime
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class TestModel(models.Model):
|
||||
text = models.CharField(max_length=10, default=_('Anything'))
|
||||
|
||||
class Company(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
date_added = models.DateTimeField(default=datetime(1799,1,31,23,59,59,0))
|
||||
|
||||
__test__ = {'API_TESTS': '''
|
||||
>>> tm = TestModel()
|
||||
>>> tm.save()
|
||||
'''
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@ import datetime
|
|||
|
||||
from django.template import Template, Context
|
||||
from django.conf import settings
|
||||
from django.utils.formats import get_format, date_format, number_format, localize
|
||||
from django.utils.formats import get_format, date_format, number_format, localize, localize_input
|
||||
from django.utils.numberformat import format
|
||||
from django.test import TestCase, client
|
||||
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy
|
||||
|
||||
from forms import I18nForm, SelectDateForm, SelectDateWidget
|
||||
from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
|
||||
|
||||
|
||||
class TranslationTests(TestCase):
|
||||
|
||||
|
@ -323,6 +324,28 @@ class FormattingTests(TestCase):
|
|||
finally:
|
||||
deactivate()
|
||||
|
||||
def test_localized_input(self):
|
||||
"""
|
||||
Tests if form input is correctly localized
|
||||
"""
|
||||
settings.USE_L10N = True
|
||||
activate('de-at')
|
||||
try:
|
||||
form6 = CompanyForm({
|
||||
'name': u'acme',
|
||||
'date_added': datetime.datetime(2009, 12, 31, 6, 0, 0),
|
||||
})
|
||||
form6.save()
|
||||
self.assertEqual(True, form6.is_valid())
|
||||
self.assertEqual(
|
||||
form6.as_ul(),
|
||||
u'<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" value="acme" maxlength="50" /></li>\n<li><label for="id_date_added">Date added:</label> <input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" /></li>'
|
||||
)
|
||||
self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00')
|
||||
self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added'])
|
||||
finally:
|
||||
deactivate()
|
||||
|
||||
class MiscTests(TestCase):
|
||||
|
||||
def test_parse_spec_http_header(self):
|
||||
|
|
Loading…
Reference in New Issue