1
0
mirror of https://github.com/django/django.git synced 2025-10-25 06:36:07 +00:00

Fixed #7980 - Improved i18n framework to support locale aware formatting (dates and numbers) and form processing.

Thanks to Marc Garcia for working on this during his Google Summer of Code 2009!

Additionally fixes #1061, #2203, #3940, #5526, #6449, #6231, #6693, #6783, #9366 and #10891.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11964 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel
2009-12-22 17:58:49 +00:00
parent 6632739e94
commit 9233d04265
128 changed files with 2304 additions and 346 deletions

View File

@@ -8,6 +8,8 @@ import re
from django.forms.widgets import Widget, Select
from django.utils.dates import MONTHS
from django.utils.safestring import mark_safe
from django.utils.formats import get_format
from django.conf import settings
__all__ = ('SelectDateWidget',)
@@ -45,38 +47,27 @@ class SelectDateWidget(Widget):
if match:
year_val, month_val, day_val = [int(v) for v in match.groups()]
choices = [(i, i) for i in self.years]
year_html = self.create_select(name, self.year_field, value, year_val, choices)
choices = MONTHS.items()
month_html = self.create_select(name, self.month_field, value, month_val, choices)
choices = [(i, i) for i in range(1, 32)]
day_html = self.create_select(name, self.day_field, value, day_val, choices)
format = get_format('DATE_FORMAT')
escaped = False
output = []
if 'id' in self.attrs:
id_ = self.attrs['id']
else:
id_ = 'id_%s' % name
month_choices = MONTHS.items()
if not (self.required and value):
month_choices.append(self.none_value)
month_choices.sort()
local_attrs = self.build_attrs(id=self.month_field % id_)
s = Select(choices=month_choices)
select_html = s.render(self.month_field % name, month_val, local_attrs)
output.append(select_html)
day_choices = [(i, i) for i in range(1, 32)]
if not (self.required and value):
day_choices.insert(0, self.none_value)
local_attrs['id'] = self.day_field % id_
s = Select(choices=day_choices)
select_html = s.render(self.day_field % name, day_val, local_attrs)
output.append(select_html)
year_choices = [(i, i) for i in self.years]
if not (self.required and value):
year_choices.insert(0, self.none_value)
local_attrs['id'] = self.year_field % id_
s = Select(choices=year_choices)
select_html = s.render(self.year_field % name, year_val, local_attrs)
output.append(select_html)
for char in format:
if escaped:
escaped = False
elif char == '\\':
escaped = True
elif char in 'Yy':
output.append(year_html)
elif char in 'bFMmNn':
output.append(month_html)
elif char in 'dj':
output.append(day_html)
return mark_safe(u'\n'.join(output))
def id_for_label(self, id_):
@@ -90,5 +81,27 @@ class SelectDateWidget(Widget):
if y == m == d == "0":
return None
if y and m and d:
return '%s-%s-%s' % (y, m, d)
if settings.USE_L10N:
input_format = get_format('DATE_INPUT_FORMATS')[0]
try:
date_value = datetime.date(int(y), int(m), int(d))
except ValueError:
pass
else:
return date_value.strftime(input_format)
else:
return '%s-%s-%s' % (y, m, d)
return data.get(name, None)
def create_select(self, name, field, value, val, choices):
if 'id' in self.attrs:
id_ = self.attrs['id']
else:
id_ = 'id_%s' % name
if not (self.required and value):
choices.insert(0, self.none_value)
local_attrs = self.build_attrs(id=field % id_)
s = Select(choices=choices)
select_html = s.render(field % name, val, local_attrs)
return select_html

View File

@@ -7,6 +7,7 @@ import os
import re
import time
import urlparse
import warnings
from decimal import Decimal, DecimalException
try:
from cStringIO import StringIO
@@ -17,6 +18,7 @@ import django.core.exceptions
import django.utils.copycompat as copy
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode, smart_str
from django.utils.formats import get_format
from util import ErrorList, ValidationError
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
@@ -36,6 +38,20 @@ __all__ = (
# These values, if given to to_python(), will trigger the self.required check.
EMPTY_VALUES = (None, '')
def en_format(name):
"""
Helper function to stay backward compatible.
"""
from django.conf.locale.en import formats
warnings.warn(
"`django.forms.fields.DEFAULT_%s` is deprecated; use `django.utils.formats.get_format('%s')` instead." % (name, name),
PendingDeprecationWarning
)
return getattr(formats, name)
DEFAULT_DATE_INPUT_FORMATS = en_format('DATE_INPUT_FORMATS')
DEFAULT_TIME_INPUT_FORMATS = en_format('TIME_INPUT_FORMATS')
DEFAULT_DATETIME_INPUT_FORMATS = en_format('DATETIME_INPUT_FORMATS')
class Field(object):
widget = TextInput # Default widget to use when rendering this type of Field.
@@ -200,7 +216,9 @@ class FloatField(Field):
if not self.required and value in EMPTY_VALUES:
return None
try:
value = float(value)
# We always accept dot as decimal separator
if isinstance(value, str) or isinstance(value, unicode):
value = float(value.replace(get_format('DECIMAL_SEPARATOR'), '.'))
except (ValueError, TypeError):
raise ValidationError(self.error_messages['invalid'])
if self.max_value is not None and value > self.max_value:
@@ -236,7 +254,9 @@ class DecimalField(Field):
return None
value = smart_str(value).strip()
try:
value = Decimal(value)
# We always accept dot as decimal separator
if isinstance(value, str) or isinstance(value, unicode):
value = Decimal(value.replace(get_format('DECIMAL_SEPARATOR'), '.'))
except DecimalException:
raise ValidationError(self.error_messages['invalid'])
@@ -264,14 +284,6 @@ class DecimalField(Field):
raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
return value
DEFAULT_DATE_INPUT_FORMATS = (
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
)
class DateField(Field):
widget = DateInput
default_error_messages = {
@@ -280,7 +292,7 @@ class DateField(Field):
def __init__(self, input_formats=None, *args, **kwargs):
super(DateField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
self.input_formats = input_formats
def clean(self, value):
"""
@@ -294,18 +306,13 @@ class DateField(Field):
return value.date()
if isinstance(value, datetime.date):
return value
for format in self.input_formats:
for format in self.input_formats or get_format('DATE_INPUT_FORMATS'):
try:
return datetime.date(*time.strptime(value, format)[:3])
except ValueError:
continue
raise ValidationError(self.error_messages['invalid'])
DEFAULT_TIME_INPUT_FORMATS = (
'%H:%M:%S', # '14:30:59'
'%H:%M', # '14:30'
)
class TimeField(Field):
widget = TimeInput
default_error_messages = {
@@ -314,7 +321,7 @@ class TimeField(Field):
def __init__(self, input_formats=None, *args, **kwargs):
super(TimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
self.input_formats = input_formats
def clean(self, value):
"""
@@ -326,25 +333,13 @@ class TimeField(Field):
return None
if isinstance(value, datetime.time):
return value
for format in self.input_formats:
for format in self.input_formats or get_format('TIME_INPUT_FORMATS'):
try:
return datetime.time(*time.strptime(value, format)[3:6])
except ValueError:
continue
raise ValidationError(self.error_messages['invalid'])
DEFAULT_DATETIME_INPUT_FORMATS = (
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
'%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
'%m/%d/%Y %H:%M', # '10/25/2006 14:30'
'%m/%d/%Y', # '10/25/2006'
'%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
'%m/%d/%y %H:%M', # '10/25/06 14:30'
'%m/%d/%y', # '10/25/06'
)
class DateTimeField(Field):
widget = DateTimeInput
default_error_messages = {
@@ -353,7 +348,7 @@ class DateTimeField(Field):
def __init__(self, input_formats=None, *args, **kwargs):
super(DateTimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
self.input_formats = input_formats
def clean(self, value):
"""
@@ -373,7 +368,7 @@ class DateTimeField(Field):
if len(value) != 2:
raise ValidationError(self.error_messages['invalid'])
value = '%s %s' % tuple(value)
for format in self.input_formats:
for format in self.input_formats or get_format('DATETIME_INPUT_FORMATS'):
try:
return datetime.datetime(*time.strptime(value, format)[:6])
except ValueError:

View File

@@ -10,6 +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 datetime import time
from util import flatatt
@@ -208,7 +209,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(value)
final_attrs['value'] = force_unicode(localize(value, is_input=True))
return mark_safe(u'<input%s />' % flatatt(final_attrs))
class TextInput(Input):