Fixed #5714 -- Strip whitespaces around date and time form field values before converting it to a native type. Thanks to SmileyChris for the initial patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16137 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
980455969e
commit
da3aa22d04
|
@ -19,7 +19,7 @@ from django.core.exceptions import ValidationError
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_unicode, smart_str
|
from django.utils.encoding import smart_unicode, smart_str, force_unicode
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazy
|
||||||
|
|
||||||
# Provide this import for backwards compatibility.
|
# Provide this import for backwards compatibility.
|
||||||
|
@ -320,16 +320,37 @@ class DecimalField(Field):
|
||||||
raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
|
raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
class DateField(Field):
|
class BaseTemporalField(Field):
|
||||||
|
|
||||||
|
def __init__(self, input_formats=None, *args, **kwargs):
|
||||||
|
super(BaseTemporalField, self).__init__(*args, **kwargs)
|
||||||
|
if input_formats is not None:
|
||||||
|
self.input_formats = input_formats
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
# Try to coerce the value to unicode.
|
||||||
|
unicode_value = force_unicode(value, strings_only=True)
|
||||||
|
if isinstance(unicode_value, unicode):
|
||||||
|
value = unicode_value.strip()
|
||||||
|
# If unicode, try to strptime against each input format.
|
||||||
|
if isinstance(value, unicode):
|
||||||
|
for format in self.input_formats:
|
||||||
|
try:
|
||||||
|
return self.strptime(value, format)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
raise ValidationError(self.error_messages['invalid'])
|
||||||
|
|
||||||
|
def strptime(self, value, format):
|
||||||
|
raise NotImplementedError('Subclasses must define this method.')
|
||||||
|
|
||||||
|
class DateField(BaseTemporalField):
|
||||||
widget = DateInput
|
widget = DateInput
|
||||||
|
input_formats = formats.get_format_lazy('DATE_INPUT_FORMATS')
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _(u'Enter a valid date.'),
|
'invalid': _(u'Enter a valid date.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, input_formats=None, *args, **kwargs):
|
|
||||||
super(DateField, self).__init__(*args, **kwargs)
|
|
||||||
self.input_formats = input_formats
|
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input can be converted to a date. Returns a Python
|
Validates that the input can be converted to a date. Returns a Python
|
||||||
|
@ -341,23 +362,18 @@ class DateField(Field):
|
||||||
return value.date()
|
return value.date()
|
||||||
if isinstance(value, datetime.date):
|
if isinstance(value, datetime.date):
|
||||||
return value
|
return value
|
||||||
for format in self.input_formats or formats.get_format('DATE_INPUT_FORMATS'):
|
return super(DateField, self).to_python(value)
|
||||||
try:
|
|
||||||
return datetime.date(*time.strptime(value, format)[:3])
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
|
||||||
|
|
||||||
class TimeField(Field):
|
def strptime(self, value, format):
|
||||||
|
return datetime.date(*time.strptime(value, format)[:3])
|
||||||
|
|
||||||
|
class TimeField(BaseTemporalField):
|
||||||
widget = TimeInput
|
widget = TimeInput
|
||||||
|
input_formats = formats.get_format_lazy('TIME_INPUT_FORMATS')
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _(u'Enter a valid time.')
|
'invalid': _(u'Enter a valid time.')
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, input_formats=None, *args, **kwargs):
|
|
||||||
super(TimeField, self).__init__(*args, **kwargs)
|
|
||||||
self.input_formats = input_formats
|
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input can be converted to a time. Returns a Python
|
Validates that the input can be converted to a time. Returns a Python
|
||||||
|
@ -367,23 +383,18 @@ class TimeField(Field):
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.time):
|
if isinstance(value, datetime.time):
|
||||||
return value
|
return value
|
||||||
for format in self.input_formats or formats.get_format('TIME_INPUT_FORMATS'):
|
return super(TimeField, self).to_python(value)
|
||||||
try:
|
|
||||||
return datetime.time(*time.strptime(value, format)[3:6])
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
|
||||||
|
|
||||||
class DateTimeField(Field):
|
def strptime(self, value, format):
|
||||||
|
return datetime.time(*time.strptime(value, format)[3:6])
|
||||||
|
|
||||||
|
class DateTimeField(BaseTemporalField):
|
||||||
widget = DateTimeInput
|
widget = DateTimeInput
|
||||||
|
input_formats = formats.get_format_lazy('DATETIME_INPUT_FORMATS')
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _(u'Enter a valid date/time.'),
|
'invalid': _(u'Enter a valid date/time.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, input_formats=None, *args, **kwargs):
|
|
||||||
super(DateTimeField, self).__init__(*args, **kwargs)
|
|
||||||
self.input_formats = input_formats
|
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input can be converted to a datetime. Returns a
|
Validates that the input can be converted to a datetime. Returns a
|
||||||
|
@ -403,12 +414,10 @@ class DateTimeField(Field):
|
||||||
if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES:
|
if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
value = '%s %s' % tuple(value)
|
value = '%s %s' % tuple(value)
|
||||||
for format in self.input_formats or formats.get_format('DATETIME_INPUT_FORMATS'):
|
return super(DateTimeField, self).to_python(value)
|
||||||
try:
|
|
||||||
return datetime.datetime(*time.strptime(value, format)[:6])
|
def strptime(self, value, format):
|
||||||
except ValueError:
|
return datetime.datetime(*time.strptime(value, format)[:6])
|
||||||
continue
|
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
|
||||||
|
|
||||||
class RegexField(CharField):
|
class RegexField(CharField):
|
||||||
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
|
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
|
||||||
|
|
|
@ -2,11 +2,12 @@ import decimal
|
||||||
import datetime
|
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 import dateformat, numberformat, datetime_safe
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.utils import dateformat, numberformat, datetime_safe
|
from django.utils.functional import lazy
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import get_language, to_locale, check_for_language
|
||||||
|
|
||||||
# format_cache is a mapping from (format_type, lang) to the format string.
|
# format_cache is a mapping from (format_type, lang) to the format string.
|
||||||
# By using the cache, it is possible to avoid running get_format_modules
|
# By using the cache, it is possible to avoid running get_format_modules
|
||||||
|
@ -81,6 +82,8 @@ def get_format(format_type, lang=None, use_l10n=None):
|
||||||
_format_cache[cache_key] = None
|
_format_cache[cache_key] = None
|
||||||
return getattr(settings, format_type)
|
return getattr(settings, format_type)
|
||||||
|
|
||||||
|
get_format_lazy = lazy(get_format, unicode, list, tuple)
|
||||||
|
|
||||||
def date_format(value, format=None, use_l10n=None):
|
def date_format(value, format=None, use_l10n=None):
|
||||||
"""
|
"""
|
||||||
Formats a datetime.date or datetime.datetime object using a
|
Formats a datetime.date or datetime.datetime object using a
|
||||||
|
|
|
@ -334,6 +334,17 @@ class FieldsTests(TestCase):
|
||||||
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '10/25/2006')
|
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '10/25/2006')
|
||||||
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '10/25/06')
|
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '10/25/06')
|
||||||
|
|
||||||
|
def test_datefield_4(self):
|
||||||
|
# Test whitespace stripping behavior (#5714)
|
||||||
|
f = DateField()
|
||||||
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 10/25/2006 '))
|
||||||
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 10/25/06 '))
|
||||||
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' Oct 25 2006 '))
|
||||||
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' October 25 2006 '))
|
||||||
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' October 25, 2006 '))
|
||||||
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 25 October 2006 '))
|
||||||
|
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, ' ')
|
||||||
|
|
||||||
# TimeField ###################################################################
|
# TimeField ###################################################################
|
||||||
|
|
||||||
def test_timefield_1(self):
|
def test_timefield_1(self):
|
||||||
|
@ -353,6 +364,13 @@ class FieldsTests(TestCase):
|
||||||
self.assertEqual(datetime.time(16, 25), f.clean('4:25 PM'))
|
self.assertEqual(datetime.time(16, 25), f.clean('4:25 PM'))
|
||||||
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, '14:30:45')
|
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, '14:30:45')
|
||||||
|
|
||||||
|
def test_timefield_3(self):
|
||||||
|
f = TimeField()
|
||||||
|
# Test whitespace stripping behavior (#5714)
|
||||||
|
self.assertEqual(datetime.time(14, 25), f.clean(' 14:25 '))
|
||||||
|
self.assertEqual(datetime.time(14, 25, 59), f.clean(' 14:25:59 '))
|
||||||
|
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, ' ')
|
||||||
|
|
||||||
# DateTimeField ###############################################################
|
# DateTimeField ###############################################################
|
||||||
|
|
||||||
def test_datetimefield_1(self):
|
def test_datetimefield_1(self):
|
||||||
|
@ -392,6 +410,18 @@ class FieldsTests(TestCase):
|
||||||
self.assertEqual(None, f.clean(''))
|
self.assertEqual(None, f.clean(''))
|
||||||
self.assertEqual('None', repr(f.clean('')))
|
self.assertEqual('None', repr(f.clean('')))
|
||||||
|
|
||||||
|
def test_datetimefield_4(self):
|
||||||
|
f = DateTimeField()
|
||||||
|
# Test whitespace stripping behavior (#5714)
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 2006-10-25 14:30:45 '))
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 2006-10-25 '))
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 10/25/2006 14:30:45 '))
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(' 10/25/2006 14:30 '))
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 10/25/2006 '))
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 10/25/06 14:30:45 '))
|
||||||
|
self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 10/25/06 '))
|
||||||
|
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date/time.']", f.clean, ' ')
|
||||||
|
|
||||||
# RegexField ##################################################################
|
# RegexField ##################################################################
|
||||||
|
|
||||||
def test_regexfield_1(self):
|
def test_regexfield_1(self):
|
||||||
|
|
Loading…
Reference in New Issue