Kaydet (Commit) bf33d3eb authored tarafından Jannis Leidel's avatar Jannis Leidel

Fixed #12444 - Date based widgets now correctly handle input values when using…

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
üst 6eb02fa9
...@@ -41,21 +41,21 @@ class FilteredSelectMultiple(forms.SelectMultiple): ...@@ -41,21 +41,21 @@ class FilteredSelectMultiple(forms.SelectMultiple):
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX)) (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
return mark_safe(u''.join(output)) return mark_safe(u''.join(output))
class AdminDateWidget(forms.TextInput): class AdminDateWidget(forms.DateTimeInput):
class Media: class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
def __init__(self, attrs={}): def __init__(self, attrs={}, format=None):
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}) super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}, format=format)
class AdminTimeWidget(forms.TextInput): class AdminTimeWidget(forms.TimeInput):
class Media: class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
def __init__(self, attrs={}): def __init__(self, attrs={}, format=None):
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}) super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}, format=format)
class AdminSplitDateTime(forms.SplitDateTimeWidget): class AdminSplitDateTime(forms.SplitDateTimeWidget):
""" """
......
...@@ -10,8 +10,7 @@ from django.utils.html import escape, conditional_escape ...@@ -10,8 +10,7 @@ from django.utils.html import escape, conditional_escape
from django.utils.translation import ugettext from django.utils.translation import ugettext
from django.utils.encoding import StrAndUnicode, force_unicode from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.formats import localize from django.utils import datetime_safe, formats
from django.utils import datetime_safe
from datetime import time from datetime import time
from util import flatatt from util import flatatt
from urlparse import urljoin from urlparse import urljoin
...@@ -209,7 +208,7 @@ class Input(Widget): ...@@ -209,7 +208,7 @@ class Input(Widget):
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
if value != '': if value != '':
# Only add the 'value' attribute if a value is non-empty. # 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)) return mark_safe(u'<input%s />' % flatatt(final_attrs))
class TextInput(Input): class TextInput(Input):
...@@ -284,7 +283,7 @@ class Textarea(Widget): ...@@ -284,7 +283,7 @@ class Textarea(Widget):
class DateInput(Input): class DateInput(Input):
input_type = 'text' input_type = 'text'
format = '%Y-%m-%d' # '2006-10-25' format = None
def __init__(self, attrs=None, format=None): def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs) super(DateInput, self).__init__(attrs)
...@@ -295,8 +294,7 @@ class DateInput(Input): ...@@ -295,8 +294,7 @@ class DateInput(Input):
if value is None: if value is None:
return '' return ''
elif hasattr(value, 'strftime'): elif hasattr(value, 'strftime'):
value = datetime_safe.new_date(value) return formats.localize_input(value, self.format)
return value.strftime(self.format)
return value return value
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
...@@ -308,7 +306,7 @@ class DateInput(Input): ...@@ -308,7 +306,7 @@ class DateInput(Input):
class DateTimeInput(Input): class DateTimeInput(Input):
input_type = 'text' 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): def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs) super(DateTimeInput, self).__init__(attrs)
...@@ -319,8 +317,7 @@ class DateTimeInput(Input): ...@@ -319,8 +317,7 @@ class DateTimeInput(Input):
if value is None: if value is None:
return '' return ''
elif hasattr(value, 'strftime'): elif hasattr(value, 'strftime'):
value = datetime_safe.new_datetime(value) return formats.localize_input(value, self.format)
return value.strftime(self.format)
return value return value
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
...@@ -332,7 +329,7 @@ class DateTimeInput(Input): ...@@ -332,7 +329,7 @@ class DateTimeInput(Input):
class TimeInput(Input): class TimeInput(Input):
input_type = 'text' input_type = 'text'
format = '%H:%M:%S' # '14:30:59' format = None
def __init__(self, attrs=None, format=None): def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs) super(TimeInput, self).__init__(attrs)
...@@ -343,7 +340,7 @@ class TimeInput(Input): ...@@ -343,7 +340,7 @@ class TimeInput(Input):
if value is None: if value is None:
return '' return ''
elif hasattr(value, 'strftime'): elif hasattr(value, 'strftime'):
return value.strftime(self.format) return formats.localize_input(value, self.format)
return value return value
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
......
...@@ -4,8 +4,8 @@ import datetime ...@@ -4,8 +4,8 @@ import datetime
from django.conf import settings from django.conf import settings
from django.utils.translation import get_language, to_locale, check_for_language from django.utils.translation import get_language, to_locale, check_for_language
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.utils import dateformat from django.utils.encoding import smart_str
from django.utils import numberformat from django.utils import dateformat, numberformat, datetime_safe
def get_format_modules(): def get_format_modules():
""" """
...@@ -65,11 +65,10 @@ def number_format(value, decimal_pos=None): ...@@ -65,11 +65,10 @@ def number_format(value, decimal_pos=None):
get_format('THOUSAND_SEPARATOR'), get_format('THOUSAND_SEPARATOR'),
) )
def localize(value, is_input=False): def localize(value):
""" """
Checks value, and if it has a localizable type (date, Checks if value is a localizable type (date, number...) and returns it
number...) it returns the value as a string using formatted as a string using current locale format
current locale format
""" """
if settings.USE_L10N: if settings.USE_L10N:
if isinstance(value, decimal.Decimal): if isinstance(value, decimal.Decimal):
...@@ -79,19 +78,27 @@ def localize(value, is_input=False): ...@@ -79,19 +78,27 @@ def localize(value, is_input=False):
elif isinstance(value, int): elif isinstance(value, int):
return number_format(value) return number_format(value)
elif isinstance(value, datetime.datetime): elif isinstance(value, datetime.datetime):
if not is_input: return date_format(value, 'DATETIME_FORMAT')
return date_format(value, 'DATETIME_FORMAT')
else:
return value.strftime(get_format('DATETIME_INPUT_FORMATS')[0])
elif isinstance(value, datetime.date): elif isinstance(value, datetime.date):
if not is_input: return date_format(value)
return date_format(value)
else:
return value.strftime(get_format('DATE_INPUT_FORMATS')[0])
elif isinstance(value, datetime.time): elif isinstance(value, datetime.time):
if not is_input: return date_format(value, 'TIME_FORMAT')
return date_format(value, 'TIME_FORMAT')
else:
return value.strftime(get_format('TIME_INPUT_FORMATS')[0])
return value 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': """ ...@@ -77,6 +77,9 @@ __test__ = {'WIDGETS_TESTS': """
>>> from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime >>> from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime
>>> from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget, ManyToManyRawIdWidget >>> from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget, ManyToManyRawIdWidget
>>> from django.contrib.admin.widgets import RelatedFieldWidgetWrapper >>> 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 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 happens in the template. This is easier than setting up a template and context
...@@ -94,6 +97,12 @@ HTML escaped. ...@@ -94,6 +97,12 @@ HTML escaped.
>>> w = AdminSplitDateTime() >>> w = AdminSplitDateTime()
>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))) >>> 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> <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') >>> band = Band.objects.create(pk=1, name='Linkin Park')
>>> album = band.album_set.create(name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg') >>> album = band.album_set.create(name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg')
......
...@@ -3,6 +3,7 @@ from django.contrib import admin ...@@ -3,6 +3,7 @@ from django.contrib import admin
from django.contrib.admin import widgets from django.contrib.admin import widgets
from unittest import TestCase from unittest import TestCase
from django.test import TestCase as DjangoTestCase from django.test import TestCase as DjangoTestCase
from django.db.models import DateField
import models import models
class AdminFormfieldForDBFieldTests(TestCase): class AdminFormfieldForDBFieldTests(TestCase):
...@@ -89,7 +90,7 @@ class AdminFormfieldForDBFieldTests(TestCase): ...@@ -89,7 +90,7 @@ class AdminFormfieldForDBFieldTests(TestCase):
def testFormfieldOverrides(self): def testFormfieldOverrides(self):
self.assertFormfield(models.Event, 'start_date', forms.TextInput, self.assertFormfield(models.Event, 'start_date', forms.TextInput,
formfield_overrides={'widget': forms.TextInput}) formfield_overrides={DateField: {'widget': forms.TextInput}})
def testFieldWithChoices(self): def testFieldWithChoices(self):
self.assertFormfield(models.Member, 'gender', forms.Select) self.assertFormfield(models.Member, 'gender', forms.Select)
......
...@@ -10,6 +10,8 @@ tests = r""" ...@@ -10,6 +10,8 @@ tests = r"""
... from decimal import Decimal ... from decimal import Decimal
... except ImportError: ... except ImportError:
... from django.utils._decimal import Decimal ... from django.utils._decimal import Decimal
>>> from django.utils.translation import activate, deactivate
>>> from django.conf import settings
########### ###########
# Widgets # # Widgets #
...@@ -1082,6 +1084,13 @@ True ...@@ -1082,6 +1084,13 @@ True
False False
>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06/05/2008', u'12:41']) >>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06/05/2008', u'12:41'])
True 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 ############################################################### # DateTimeInput ###############################################################
...@@ -1099,6 +1108,12 @@ u'<input type="text" name="date" value="2007-09-17 12:51:34" />' ...@@ -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" />' u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51)) >>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="text" name="date" value="2007-09-17 12:51:00" />' 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. Use 'format' to change the way a value is displayed.
>>> w = DateTimeInput(format='%d/%m/%Y %H:%M') >>> w = DateTimeInput(format='%d/%m/%Y %H:%M')
...@@ -1107,6 +1122,7 @@ u'<input type="text" name="date" value="17/09/2007 12:51" />' ...@@ -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') >>> w._has_changed(d, '17/09/2007 12:51')
False False
# DateInput ################################################################### # DateInput ###################################################################
>>> w = DateInput() >>> w = DateInput()
...@@ -1125,6 +1141,13 @@ We should be able to initialize from a unicode value. ...@@ -1125,6 +1141,13 @@ We should be able to initialize from a unicode value.
>>> w.render('date', u'2007-09-17') >>> w.render('date', u'2007-09-17')
u'<input type="text" name="date" value="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. Use 'format' to change the way a value is displayed.
>>> w = DateInput(format='%d/%m/%Y') >>> w = DateInput(format='%d/%m/%Y')
>>> w.render('date', d) >>> w.render('date', d)
...@@ -1153,6 +1176,13 @@ We should be able to initialize from a unicode value. ...@@ -1153,6 +1176,13 @@ We should be able to initialize from a unicode value.
>>> w.render('time', u'13:12:11') >>> w.render('time', u'13:12:11')
u'<input type="text" name="time" value="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. Use 'format' to change the way a value is displayed.
>>> w = TimeInput(format='%H:%M') >>> w = TimeInput(format='%H:%M')
>>> w.render('time', t) >>> w.render('time', t)
...@@ -1176,6 +1206,12 @@ u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" n ...@@ -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" />' 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)) >>> 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" />' 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
""" """
from django import template, forms from django import template, forms
from django.forms.extras import SelectDateWidget from django.forms.extras import SelectDateWidget
from models import Company
class I18nForm(forms.Form): class I18nForm(forms.Form):
decimal_field = forms.DecimalField() decimal_field = forms.DecimalField()
...@@ -11,3 +12,6 @@ class I18nForm(forms.Form): ...@@ -11,3 +12,6 @@ class I18nForm(forms.Form):
class SelectDateForm(forms.Form): class SelectDateForm(forms.Form):
date_field = forms.DateField(widget=SelectDateWidget) date_field = forms.DateField(widget=SelectDateWidget)
class CompanyForm(forms.ModelForm):
class Meta:
model = Company
from datetime import datetime
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class TestModel(models.Model): class TestModel(models.Model):
text = models.CharField(max_length=10, default=_('Anything')) 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': ''' __test__ = {'API_TESTS': '''
>>> tm = TestModel() >>> tm = TestModel()
>>> tm.save() >>> tm.save()
''' '''
} }
...@@ -4,12 +4,13 @@ import datetime ...@@ -4,12 +4,13 @@ import datetime
from django.template import Template, Context from django.template import Template, Context
from django.conf import settings 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.utils.numberformat import format
from django.test import TestCase, client from django.test import TestCase, client
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy 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): class TranslationTests(TestCase):
...@@ -323,6 +324,28 @@ class FormattingTests(TestCase): ...@@ -323,6 +324,28 @@ class FormattingTests(TestCase):
finally: finally:
deactivate() 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): class MiscTests(TestCase):
def test_parse_spec_http_header(self): def test_parse_spec_http_header(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment