diff --git a/django/forms/fields.py b/django/forms/fields.py index b31fe805aef..14152c81a0b 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -71,7 +71,7 @@ class Field(object): def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None, error_messages=None, show_hidden_initial=False, - validators=[]): + validators=[], localize=False): # required -- Boolean that specifies whether the field is required. # True by default. # widget -- A Widget class, or instance of a Widget class, that should @@ -85,9 +85,12 @@ class Field(object): # initial -- A value to use in this Field's initial display. This value # is *not* used as a fallback if data isn't given. # help_text -- An optional string to use as "help text" for this Field. + # error_messages -- An optional dictionary to override the default + # messages that the field will raise. # show_hidden_initial -- Boolean that specifies if it is needed to render a # hidden widget with initial value after widget. # validators -- List of addtional validators to use + # localize -- Boolean that specifies if the field should be localized. if label is not None: label = smart_unicode(label) self.required, self.label, self.initial = required, label, initial @@ -100,6 +103,9 @@ class Field(object): if isinstance(widget, type): widget = widget() + # Trigger the localization machinery if needed. + self.localize = localize + # Hook into self.widget_attrs() for any Field-specific HTML attributes. extra_attrs = self.widget_attrs(widget) if extra_attrs: @@ -119,6 +125,9 @@ class Field(object): self.validators = self.default_validators + validators + def localize_value(self, value): + return formats.localize_input(value) + def to_python(self, value): return value @@ -213,7 +222,8 @@ class IntegerField(Field): value = super(IntegerField, self).to_python(value) if value in validators.EMPTY_VALUES: return None - value = formats.sanitize_separators(value) + if self.localize: + value = formats.sanitize_separators(value) try: value = int(str(value)) except (ValueError, TypeError): @@ -233,7 +243,8 @@ class FloatField(IntegerField): value = super(IntegerField, self).to_python(value) if value in validators.EMPTY_VALUES: return None - value = formats.sanitize_separators(value) + if self.localize: + value = formats.sanitize_separators(value) try: value = float(value) except (ValueError, TypeError): @@ -268,7 +279,8 @@ class DecimalField(Field): """ if value in validators.EMPTY_VALUES: return None - value = formats.sanitize_separators(value) + if self.localize: + value = formats.sanitize_separators(value) value = smart_str(value).strip() try: value = Decimal(value) diff --git a/django/forms/forms.py b/django/forms/forms.py index b3718efa9a6..6076065b597 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -443,6 +443,8 @@ class BoundField(StrAndUnicode): name = self.html_name else: name = self.html_initial_name + if self.field.localize: + data = self.field.localize_value(data) return widget.render(name, data, attrs=attrs) def as_text(self, attrs=None, **kwargs): diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 8b4112bec5e..082c11b9117 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -13,7 +13,6 @@ from django.utils.safestring import mark_safe from django.utils import formats import time import datetime -from django.utils.formats import get_format from util import flatatt from urlparse import urljoin @@ -214,7 +213,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(formats.localize_input(value)) + final_attrs['value'] = force_unicode(value) return mark_safe(u'' % flatatt(final_attrs)) class TextInput(Input): @@ -319,7 +318,7 @@ class DateInput(Input): # formatted by HiddenInput using formats.localize_input, which is not # necessarily the format used for this widget. Attempt to convert it. try: - input_format = get_format('DATE_INPUT_FORMATS')[0] + input_format = formats.get_format('DATE_INPUT_FORMATS')[0] initial = datetime.date(*time.strptime(initial, input_format)[:3]) except (TypeError, ValueError): pass @@ -350,7 +349,7 @@ class DateTimeInput(Input): # formatted by HiddenInput using formats.localize_input, which is not # necessarily the format used for this widget. Attempt to convert it. try: - input_format = get_format('DATETIME_INPUT_FORMATS')[0] + input_format = formats.get_format('DATETIME_INPUT_FORMATS')[0] initial = datetime.datetime(*time.strptime(initial, input_format)[:6]) except (TypeError, ValueError): pass @@ -381,7 +380,7 @@ class TimeInput(Input): # formatted by HiddenInput using formats.localize_input, which is not # necessarily the format used for this widget. Attempt to convert it. try: - input_format = get_format('TIME_INPUT_FORMATS')[0] + input_format = formats.get_format('TIME_INPUT_FORMATS')[0] initial = datetime.time(*time.strptime(initial, input_format)[3:6]) except (TypeError, ValueError): pass @@ -771,6 +770,8 @@ class SplitHiddenDateTimeWidget(SplitDateTimeWidget): """ is_hidden = True - def __init__(self, attrs=None): - widgets = (HiddenInput(attrs=attrs), HiddenInput(attrs=attrs)) - super(SplitDateTimeWidget, self).__init__(widgets, attrs) + def __init__(self, attrs=None, date_format=None, time_format=None): + super(SplitHiddenDateTimeWidget, self).__init__(attrs, date_format, time_format) + for widget in self.widgets: + widget.input_type = 'hidden' + widget.is_hidden = True diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index 22fabb3fa8b..396e51046e8 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -259,6 +259,7 @@ error message keys it uses. ``validators`` ~~~~~~~~~~~~~~ + .. versionadded:: 1.2 .. attribute:: Field.validators @@ -268,6 +269,20 @@ for this field. See the :ref:`validators documentation ` for more information. +``localize`` +~~~~~~~~~~~~ + +.. versionadded:: 1.2 + +.. attribute:: Field.localize + +The ``localize`` argument enables the localization of form data, input as well +as the rendered output. + +See the :ref:`format localization ` documentation for +more information. + + Built-in ``Field`` classes -------------------------- diff --git a/docs/topics/i18n/localization.txt b/docs/topics/i18n/localization.txt index 10e59230e0e..8e1beaef46f 100644 --- a/docs/topics/i18n/localization.txt +++ b/docs/topics/i18n/localization.txt @@ -268,6 +268,13 @@ Django uses different formats for different locales when guessing the format used by the user when inputting data on forms. Note that Django uses different formats for displaying data, and for parsing it. +To enable a form field to localize input and output data simply use its +``localize`` argument:: + + class CashRegisterForm(forms.Form): + product = forms.CharField() + revenue = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) + Creating custom format files ---------------------------- diff --git a/tests/regressiontests/i18n/forms.py b/tests/regressiontests/i18n/forms.py index 8ed834c9d03..216d1fec01e 100644 --- a/tests/regressiontests/i18n/forms.py +++ b/tests/regressiontests/i18n/forms.py @@ -3,16 +3,19 @@ from django.forms.extras import SelectDateWidget from models import Company class I18nForm(forms.Form): - decimal_field = forms.DecimalField() - float_field = forms.FloatField() - date_field = forms.DateField() - datetime_field = forms.DateTimeField() - time_field = forms.TimeField() - integer_field = forms.IntegerField() + decimal_field = forms.DecimalField(localize=True) + float_field = forms.FloatField(localize=True) + date_field = forms.DateField(localize=True) + datetime_field = forms.DateTimeField(localize=True) + time_field = forms.TimeField(localize=True) + integer_field = forms.IntegerField(localize=True) class SelectDateForm(forms.Form): date_field = forms.DateField(widget=SelectDateWidget) class CompanyForm(forms.ModelForm): + cents_payed = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) + products_delivered = forms.IntegerField(localize=True) + class Meta: model = Company diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 941f66f3eba..17e53dfa29a 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -409,7 +409,8 @@ class FormattingTests(TestCase): 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']) settings.USE_THOUSAND_SEPARATOR = True - self.assert_(u'12.000' in form6.as_ul()) + # Checking for the localized "products_delivered" field + self.assert_(u'' in form6.as_ul()) finally: deactivate()