diff --git a/django/forms/fields.py b/django/forms/fields.py
index b31fe80..14152c8 100644
a
|
b
|
class Field(object):
|
71 | 71 | |
72 | 72 | def __init__(self, required=True, widget=None, label=None, initial=None, |
73 | 73 | help_text=None, error_messages=None, show_hidden_initial=False, |
74 | | validators=[]): |
| 74 | validators=[], localize=False): |
75 | 75 | # required -- Boolean that specifies whether the field is required. |
76 | 76 | # True by default. |
77 | 77 | # widget -- A Widget class, or instance of a Widget class, that should |
… |
… |
class Field(object):
|
85 | 85 | # initial -- A value to use in this Field's initial display. This value |
86 | 86 | # is *not* used as a fallback if data isn't given. |
87 | 87 | # help_text -- An optional string to use as "help text" for this Field. |
| 88 | # error_messages -- An optional dictionary to override the default |
| 89 | # messages that the field will raise. |
88 | 90 | # show_hidden_initial -- Boolean that specifies if it is needed to render a |
89 | 91 | # hidden widget with initial value after widget. |
90 | 92 | # validators -- List of addtional validators to use |
| 93 | # localize -- Boolean that specifies if the field should be localized. |
91 | 94 | if label is not None: |
92 | 95 | label = smart_unicode(label) |
93 | 96 | self.required, self.label, self.initial = required, label, initial |
… |
… |
class Field(object):
|
100 | 103 | if isinstance(widget, type): |
101 | 104 | widget = widget() |
102 | 105 | |
| 106 | # Trigger the localization machinery if needed. |
| 107 | self.localize = localize |
| 108 | |
103 | 109 | # Hook into self.widget_attrs() for any Field-specific HTML attributes. |
104 | 110 | extra_attrs = self.widget_attrs(widget) |
105 | 111 | if extra_attrs: |
… |
… |
class Field(object):
|
119 | 125 | |
120 | 126 | self.validators = self.default_validators + validators |
121 | 127 | |
| 128 | def localize_value(self, value): |
| 129 | return formats.localize_input(value) |
| 130 | |
122 | 131 | def to_python(self, value): |
123 | 132 | return value |
124 | 133 | |
… |
… |
class IntegerField(Field):
|
213 | 222 | value = super(IntegerField, self).to_python(value) |
214 | 223 | if value in validators.EMPTY_VALUES: |
215 | 224 | return None |
216 | | value = formats.sanitize_separators(value) |
| 225 | if self.localize: |
| 226 | value = formats.sanitize_separators(value) |
217 | 227 | try: |
218 | 228 | value = int(str(value)) |
219 | 229 | except (ValueError, TypeError): |
… |
… |
class FloatField(IntegerField):
|
233 | 243 | value = super(IntegerField, self).to_python(value) |
234 | 244 | if value in validators.EMPTY_VALUES: |
235 | 245 | return None |
236 | | value = formats.sanitize_separators(value) |
| 246 | if self.localize: |
| 247 | value = formats.sanitize_separators(value) |
237 | 248 | try: |
238 | 249 | value = float(value) |
239 | 250 | except (ValueError, TypeError): |
… |
… |
class DecimalField(Field):
|
268 | 279 | """ |
269 | 280 | if value in validators.EMPTY_VALUES: |
270 | 281 | return None |
271 | | value = formats.sanitize_separators(value) |
| 282 | if self.localize: |
| 283 | value = formats.sanitize_separators(value) |
272 | 284 | value = smart_str(value).strip() |
273 | 285 | try: |
274 | 286 | value = Decimal(value) |
diff --git a/django/forms/forms.py b/django/forms/forms.py
index b3718ef..6076065 100644
a
|
b
|
class BoundField(StrAndUnicode):
|
443 | 443 | name = self.html_name |
444 | 444 | else: |
445 | 445 | name = self.html_initial_name |
| 446 | if self.field.localize: |
| 447 | data = self.field.localize_value(data) |
446 | 448 | return widget.render(name, data, attrs=attrs) |
447 | 449 | |
448 | 450 | def as_text(self, attrs=None, **kwargs): |
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 8b4112b..082c11b 100644
a
|
b
|
from django.utils.safestring import mark_safe
|
13 | 13 | from django.utils import formats |
14 | 14 | import time |
15 | 15 | import datetime |
16 | | from django.utils.formats import get_format |
17 | 16 | from util import flatatt |
18 | 17 | from urlparse import urljoin |
19 | 18 | |
… |
… |
class Input(Widget):
|
214 | 213 | final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) |
215 | 214 | if value != '': |
216 | 215 | # Only add the 'value' attribute if a value is non-empty. |
217 | | final_attrs['value'] = force_unicode(formats.localize_input(value)) |
| 216 | final_attrs['value'] = force_unicode(value) |
218 | 217 | return mark_safe(u'<input%s />' % flatatt(final_attrs)) |
219 | 218 | |
220 | 219 | class TextInput(Input): |
… |
… |
class DateInput(Input):
|
319 | 318 | # formatted by HiddenInput using formats.localize_input, which is not |
320 | 319 | # necessarily the format used for this widget. Attempt to convert it. |
321 | 320 | try: |
322 | | input_format = get_format('DATE_INPUT_FORMATS')[0] |
| 321 | input_format = formats.get_format('DATE_INPUT_FORMATS')[0] |
323 | 322 | initial = datetime.date(*time.strptime(initial, input_format)[:3]) |
324 | 323 | except (TypeError, ValueError): |
325 | 324 | pass |
… |
… |
class DateTimeInput(Input):
|
350 | 349 | # formatted by HiddenInput using formats.localize_input, which is not |
351 | 350 | # necessarily the format used for this widget. Attempt to convert it. |
352 | 351 | try: |
353 | | input_format = get_format('DATETIME_INPUT_FORMATS')[0] |
| 352 | input_format = formats.get_format('DATETIME_INPUT_FORMATS')[0] |
354 | 353 | initial = datetime.datetime(*time.strptime(initial, input_format)[:6]) |
355 | 354 | except (TypeError, ValueError): |
356 | 355 | pass |
… |
… |
class TimeInput(Input):
|
381 | 380 | # formatted by HiddenInput using formats.localize_input, which is not |
382 | 381 | # necessarily the format used for this widget. Attempt to convert it. |
383 | 382 | try: |
384 | | input_format = get_format('TIME_INPUT_FORMATS')[0] |
| 383 | input_format = formats.get_format('TIME_INPUT_FORMATS')[0] |
385 | 384 | initial = datetime.time(*time.strptime(initial, input_format)[3:6]) |
386 | 385 | except (TypeError, ValueError): |
387 | 386 | pass |
… |
… |
class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
|
771 | 770 | """ |
772 | 771 | is_hidden = True |
773 | 772 | |
774 | | def __init__(self, attrs=None): |
775 | | widgets = (HiddenInput(attrs=attrs), HiddenInput(attrs=attrs)) |
776 | | super(SplitDateTimeWidget, self).__init__(widgets, attrs) |
| 773 | def __init__(self, attrs=None, date_format=None, time_format=None): |
| 774 | super(SplitHiddenDateTimeWidget, self).__init__(attrs, date_format, time_format) |
| 775 | for widget in self.widgets: |
| 776 | widget.input_type = 'hidden' |
| 777 | widget.is_hidden = True |
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index 22fabb3..396e510 100644
a
|
b
|
error message keys it uses.
|
259 | 259 | |
260 | 260 | ``validators`` |
261 | 261 | ~~~~~~~~~~~~~~ |
| 262 | |
262 | 263 | .. versionadded:: 1.2 |
263 | 264 | |
264 | 265 | .. attribute:: Field.validators |
… |
… |
for this field.
|
268 | 269 | |
269 | 270 | See the :ref:`validators documentation <ref-validators>` for more information. |
270 | 271 | |
| 272 | ``localize`` |
| 273 | ~~~~~~~~~~~~ |
| 274 | |
| 275 | .. versionadded:: 1.2 |
| 276 | |
| 277 | .. attribute:: Field.localize |
| 278 | |
| 279 | The ``localize`` argument enables the localization of form data, input as well |
| 280 | as the rendered output. |
| 281 | |
| 282 | See the :ref:`format localization <format-localization>` documentation for |
| 283 | more information. |
| 284 | |
| 285 | |
271 | 286 | Built-in ``Field`` classes |
272 | 287 | -------------------------- |
273 | 288 | |
diff --git a/docs/topics/i18n/localization.txt b/docs/topics/i18n/localization.txt
index 10e5923..8e1beae 100644
a
|
b
|
Django uses different formats for different locales when guessing the format
|
268 | 268 | used by the user when inputting data on forms. Note that Django uses different |
269 | 269 | formats for displaying data, and for parsing it. |
270 | 270 | |
| 271 | To enable a form field to localize input and output data simply use its |
| 272 | ``localize`` argument:: |
| 273 | |
| 274 | class CashRegisterForm(forms.Form): |
| 275 | product = forms.CharField() |
| 276 | revenue = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) |
| 277 | |
271 | 278 | Creating custom format files |
272 | 279 | ---------------------------- |
273 | 280 | |
diff --git a/tests/regressiontests/i18n/forms.py b/tests/regressiontests/i18n/forms.py
index 8ed834c..216d1fe 100644
a
|
b
|
from django.forms.extras import SelectDateWidget
|
3 | 3 | from models import Company |
4 | 4 | |
5 | 5 | class I18nForm(forms.Form): |
6 | | decimal_field = forms.DecimalField() |
7 | | float_field = forms.FloatField() |
8 | | date_field = forms.DateField() |
9 | | datetime_field = forms.DateTimeField() |
10 | | time_field = forms.TimeField() |
11 | | integer_field = forms.IntegerField() |
| 6 | decimal_field = forms.DecimalField(localize=True) |
| 7 | float_field = forms.FloatField(localize=True) |
| 8 | date_field = forms.DateField(localize=True) |
| 9 | datetime_field = forms.DateTimeField(localize=True) |
| 10 | time_field = forms.TimeField(localize=True) |
| 11 | integer_field = forms.IntegerField(localize=True) |
12 | 12 | |
13 | 13 | class SelectDateForm(forms.Form): |
14 | 14 | date_field = forms.DateField(widget=SelectDateWidget) |
15 | 15 | |
16 | 16 | class CompanyForm(forms.ModelForm): |
| 17 | cents_payed = forms.DecimalField(max_digits=4, decimal_places=2, localize=True) |
| 18 | products_delivered = forms.IntegerField(localize=True) |
| 19 | |
17 | 20 | class Meta: |
18 | 21 | model = Company |
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index 31150a6..9cc1089 100644
a
|
b
|
class FormattingTests(TestCase):
|
410 | 410 | self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00') |
411 | 411 | self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added']) |
412 | 412 | settings.USE_THOUSAND_SEPARATOR = True |
413 | | self.assert_(u'12.000' in form6.as_ul()) |
| 413 | # Checking for the localized "products_delivered" field |
| 414 | self.assert_(u'<input type="text" name="products_delivered" value="12.000" id="id_products_delivered" />' in form6.as_ul()) |
414 | 415 | finally: |
415 | 416 | deactivate() |
416 | 417 | |