Fixed #13032 - Added localize parameter to form fields to be able to selectively enable localization.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12867 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-03-27 16:43:27 +00:00
parent ad5afd6ed2
commit ca4c6f65ea
7 changed files with 60 additions and 19 deletions

View File

@ -71,7 +71,7 @@ class Field(object):
def __init__(self, required=True, widget=None, label=None, initial=None, def __init__(self, required=True, widget=None, label=None, initial=None,
help_text=None, error_messages=None, show_hidden_initial=False, help_text=None, error_messages=None, show_hidden_initial=False,
validators=[]): validators=[], localize=False):
# required -- Boolean that specifies whether the field is required. # required -- Boolean that specifies whether the field is required.
# True by default. # True by default.
# widget -- A Widget class, or instance of a Widget class, that should # 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 # initial -- A value to use in this Field's initial display. This value
# is *not* used as a fallback if data isn't given. # is *not* used as a fallback if data isn't given.
# help_text -- An optional string to use as "help text" for this Field. # 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 # show_hidden_initial -- Boolean that specifies if it is needed to render a
# hidden widget with initial value after widget. # hidden widget with initial value after widget.
# validators -- List of addtional validators to use # validators -- List of addtional validators to use
# localize -- Boolean that specifies if the field should be localized.
if label is not None: if label is not None:
label = smart_unicode(label) label = smart_unicode(label)
self.required, self.label, self.initial = required, label, initial self.required, self.label, self.initial = required, label, initial
@ -100,6 +103,9 @@ class Field(object):
if isinstance(widget, type): if isinstance(widget, type):
widget = widget() widget = widget()
# Trigger the localization machinery if needed.
self.localize = localize
# Hook into self.widget_attrs() for any Field-specific HTML attributes. # Hook into self.widget_attrs() for any Field-specific HTML attributes.
extra_attrs = self.widget_attrs(widget) extra_attrs = self.widget_attrs(widget)
if extra_attrs: if extra_attrs:
@ -119,6 +125,9 @@ class Field(object):
self.validators = self.default_validators + validators self.validators = self.default_validators + validators
def localize_value(self, value):
return formats.localize_input(value)
def to_python(self, value): def to_python(self, value):
return value return value
@ -213,7 +222,8 @@ class IntegerField(Field):
value = super(IntegerField, self).to_python(value) value = super(IntegerField, self).to_python(value)
if value in validators.EMPTY_VALUES: if value in validators.EMPTY_VALUES:
return None return None
value = formats.sanitize_separators(value) if self.localize:
value = formats.sanitize_separators(value)
try: try:
value = int(str(value)) value = int(str(value))
except (ValueError, TypeError): except (ValueError, TypeError):
@ -233,7 +243,8 @@ class FloatField(IntegerField):
value = super(IntegerField, self).to_python(value) value = super(IntegerField, self).to_python(value)
if value in validators.EMPTY_VALUES: if value in validators.EMPTY_VALUES:
return None return None
value = formats.sanitize_separators(value) if self.localize:
value = formats.sanitize_separators(value)
try: try:
value = float(value) value = float(value)
except (ValueError, TypeError): except (ValueError, TypeError):
@ -268,7 +279,8 @@ class DecimalField(Field):
""" """
if value in validators.EMPTY_VALUES: if value in validators.EMPTY_VALUES:
return None return None
value = formats.sanitize_separators(value) if self.localize:
value = formats.sanitize_separators(value)
value = smart_str(value).strip() value = smart_str(value).strip()
try: try:
value = Decimal(value) value = Decimal(value)

View File

@ -443,6 +443,8 @@ class BoundField(StrAndUnicode):
name = self.html_name name = self.html_name
else: else:
name = self.html_initial_name name = self.html_initial_name
if self.field.localize:
data = self.field.localize_value(data)
return widget.render(name, data, attrs=attrs) return widget.render(name, data, attrs=attrs)
def as_text(self, attrs=None, **kwargs): def as_text(self, attrs=None, **kwargs):

View File

@ -13,7 +13,6 @@ from django.utils.safestring import mark_safe
from django.utils import formats from django.utils import formats
import time import time
import datetime import datetime
from django.utils.formats import get_format
from util import flatatt from util import flatatt
from urlparse import urljoin from urlparse import urljoin
@ -214,7 +213,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(formats.localize_input(value)) final_attrs['value'] = force_unicode(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):
@ -319,7 +318,7 @@ class DateInput(Input):
# formatted by HiddenInput using formats.localize_input, which is not # formatted by HiddenInput using formats.localize_input, which is not
# necessarily the format used for this widget. Attempt to convert it. # necessarily the format used for this widget. Attempt to convert it.
try: 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]) initial = datetime.date(*time.strptime(initial, input_format)[:3])
except (TypeError, ValueError): except (TypeError, ValueError):
pass pass
@ -350,7 +349,7 @@ class DateTimeInput(Input):
# formatted by HiddenInput using formats.localize_input, which is not # formatted by HiddenInput using formats.localize_input, which is not
# necessarily the format used for this widget. Attempt to convert it. # necessarily the format used for this widget. Attempt to convert it.
try: 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]) initial = datetime.datetime(*time.strptime(initial, input_format)[:6])
except (TypeError, ValueError): except (TypeError, ValueError):
pass pass
@ -381,7 +380,7 @@ class TimeInput(Input):
# formatted by HiddenInput using formats.localize_input, which is not # formatted by HiddenInput using formats.localize_input, which is not
# necessarily the format used for this widget. Attempt to convert it. # necessarily the format used for this widget. Attempt to convert it.
try: 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]) initial = datetime.time(*time.strptime(initial, input_format)[3:6])
except (TypeError, ValueError): except (TypeError, ValueError):
pass pass
@ -771,6 +770,8 @@ class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
""" """
is_hidden = True is_hidden = True
def __init__(self, attrs=None): def __init__(self, attrs=None, date_format=None, time_format=None):
widgets = (HiddenInput(attrs=attrs), HiddenInput(attrs=attrs)) super(SplitHiddenDateTimeWidget, self).__init__(attrs, date_format, time_format)
super(SplitDateTimeWidget, self).__init__(widgets, attrs) for widget in self.widgets:
widget.input_type = 'hidden'
widget.is_hidden = True

View File

@ -259,6 +259,7 @@ error message keys it uses.
``validators`` ``validators``
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
.. versionadded:: 1.2 .. versionadded:: 1.2
.. attribute:: Field.validators .. attribute:: Field.validators
@ -268,6 +269,20 @@ for this field.
See the :ref:`validators documentation <ref-validators>` for more information. See the :ref:`validators documentation <ref-validators>` 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 <format-localization>` documentation for
more information.
Built-in ``Field`` classes Built-in ``Field`` classes
-------------------------- --------------------------

View File

@ -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 used by the user when inputting data on forms. Note that Django uses different
formats for displaying data, and for parsing it. 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 Creating custom format files
---------------------------- ----------------------------

View File

@ -3,16 +3,19 @@ from django.forms.extras import SelectDateWidget
from models import Company from models import Company
class I18nForm(forms.Form): class I18nForm(forms.Form):
decimal_field = forms.DecimalField() decimal_field = forms.DecimalField(localize=True)
float_field = forms.FloatField() float_field = forms.FloatField(localize=True)
date_field = forms.DateField() date_field = forms.DateField(localize=True)
datetime_field = forms.DateTimeField() datetime_field = forms.DateTimeField(localize=True)
time_field = forms.TimeField() time_field = forms.TimeField(localize=True)
integer_field = forms.IntegerField() integer_field = forms.IntegerField(localize=True)
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 CompanyForm(forms.ModelForm):
cents_payed = forms.DecimalField(max_digits=4, decimal_places=2, localize=True)
products_delivered = forms.IntegerField(localize=True)
class Meta: class Meta:
model = Company model = Company

View File

@ -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(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']) self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added'])
settings.USE_THOUSAND_SEPARATOR = True settings.USE_THOUSAND_SEPARATOR = True
self.assert_(u'12.000' in form6.as_ul()) # Checking for the localized "products_delivered" field
self.assert_(u'<input type="text" name="products_delivered" value="12.000" id="id_products_delivered" />' in form6.as_ul())
finally: finally:
deactivate() deactivate()