Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #5714 -- Strip whitespaces around date and time form field valu…

…es 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
  • Loading branch information...
commit da3aa22d04d6452f87abbb1a0fee8a90a61eff5b 1 parent 9804559
Jannis Leidel authored May 01, 2011
77  django/forms/fields.py
@@ -19,7 +19,7 @@
19 19
 from django.core import validators
20 20
 from django.utils import formats
21 21
 from django.utils.translation import ugettext_lazy as _
22  
-from django.utils.encoding import smart_unicode, smart_str
  22
+from django.utils.encoding import smart_unicode, smart_str, force_unicode
23 23
 from django.utils.functional import lazy
24 24
 
25 25
 # Provide this import for backwards compatibility.
@@ -320,16 +320,37 @@ def validate(self, value):
320 320
             raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
321 321
         return value
322 322
 
323  
-class DateField(Field):
  323
+class BaseTemporalField(Field):
  324
+
  325
+    def __init__(self, input_formats=None, *args, **kwargs):
  326
+        super(BaseTemporalField, self).__init__(*args, **kwargs)
  327
+        if input_formats is not None:
  328
+            self.input_formats = input_formats
  329
+
  330
+    def to_python(self, value):
  331
+        # Try to coerce the value to unicode.
  332
+        unicode_value = force_unicode(value, strings_only=True)
  333
+        if isinstance(unicode_value, unicode):
  334
+            value = unicode_value.strip()
  335
+        # If unicode, try to strptime against each input format.
  336
+        if isinstance(value, unicode):
  337
+            for format in self.input_formats:
  338
+                try:
  339
+                    return self.strptime(value, format)
  340
+                except ValueError:
  341
+                    continue
  342
+        raise ValidationError(self.error_messages['invalid'])
  343
+
  344
+    def strptime(self, value, format):
  345
+        raise NotImplementedError('Subclasses must define this method.')
  346
+
  347
+class DateField(BaseTemporalField):
324 348
     widget = DateInput
  349
+    input_formats = formats.get_format_lazy('DATE_INPUT_FORMATS')
325 350
     default_error_messages = {
326 351
         'invalid': _(u'Enter a valid date.'),
327 352
     }
328 353
 
329  
-    def __init__(self, input_formats=None, *args, **kwargs):
330  
-        super(DateField, self).__init__(*args, **kwargs)
331  
-        self.input_formats = input_formats
332  
-
333 354
     def to_python(self, value):
334 355
         """
335 356
         Validates that the input can be converted to a date. Returns a Python
@@ -341,23 +362,18 @@ def to_python(self, value):
341 362
             return value.date()
342 363
         if isinstance(value, datetime.date):
343 364
             return value
344  
-        for format in self.input_formats or formats.get_format('DATE_INPUT_FORMATS'):
345  
-            try:
346  
-                return datetime.date(*time.strptime(value, format)[:3])
347  
-            except ValueError:
348  
-                continue
349  
-        raise ValidationError(self.error_messages['invalid'])
  365
+        return super(DateField, self).to_python(value)
350 366
 
351  
-class TimeField(Field):
  367
+    def strptime(self, value, format):
  368
+        return datetime.date(*time.strptime(value, format)[:3])
  369
+
  370
+class TimeField(BaseTemporalField):
352 371
     widget = TimeInput
  372
+    input_formats = formats.get_format_lazy('TIME_INPUT_FORMATS')
353 373
     default_error_messages = {
354 374
         'invalid': _(u'Enter a valid time.')
355 375
     }
356 376
 
357  
-    def __init__(self, input_formats=None, *args, **kwargs):
358  
-        super(TimeField, self).__init__(*args, **kwargs)
359  
-        self.input_formats = input_formats
360  
-
361 377
     def to_python(self, value):
362 378
         """
363 379
         Validates that the input can be converted to a time. Returns a Python
@@ -367,23 +383,18 @@ def to_python(self, value):
367 383
             return None
368 384
         if isinstance(value, datetime.time):
369 385
             return value
370  
-        for format in self.input_formats or formats.get_format('TIME_INPUT_FORMATS'):
371  
-            try:
372  
-                return datetime.time(*time.strptime(value, format)[3:6])
373  
-            except ValueError:
374  
-                continue
375  
-        raise ValidationError(self.error_messages['invalid'])
  386
+        return super(TimeField, self).to_python(value)
  387
+
  388
+    def strptime(self, value, format):
  389
+        return datetime.time(*time.strptime(value, format)[3:6])
376 390
 
377  
-class DateTimeField(Field):
  391
+class DateTimeField(BaseTemporalField):
378 392
     widget = DateTimeInput
  393
+    input_formats = formats.get_format_lazy('DATETIME_INPUT_FORMATS')
379 394
     default_error_messages = {
380 395
         'invalid': _(u'Enter a valid date/time.'),
381 396
     }
382 397
 
383  
-    def __init__(self, input_formats=None, *args, **kwargs):
384  
-        super(DateTimeField, self).__init__(*args, **kwargs)
385  
-        self.input_formats = input_formats
386  
-
387 398
     def to_python(self, value):
388 399
         """
389 400
         Validates that the input can be converted to a datetime. Returns a
@@ -403,12 +414,10 @@ def to_python(self, value):
403 414
             if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES:
404 415
                 return None
405 416
             value = '%s %s' % tuple(value)
406  
-        for format in self.input_formats or formats.get_format('DATETIME_INPUT_FORMATS'):
407  
-            try:
408  
-                return datetime.datetime(*time.strptime(value, format)[:6])
409  
-            except ValueError:
410  
-                continue
411  
-        raise ValidationError(self.error_messages['invalid'])
  417
+        return super(DateTimeField, self).to_python(value)
  418
+
  419
+    def strptime(self, value, format):
  420
+        return datetime.datetime(*time.strptime(value, format)[:6])
412 421
 
413 422
 class RegexField(CharField):
414 423
     def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
7  django/utils/formats.py
@@ -2,11 +2,12 @@
2 2
 import datetime
3 3
 
4 4
 from django.conf import settings
5  
-from django.utils.translation import get_language, to_locale, check_for_language
  5
+from django.utils import dateformat, numberformat, datetime_safe
6 6
 from django.utils.importlib import import_module
7 7
 from django.utils.encoding import smart_str
8  
-from django.utils import dateformat, numberformat, datetime_safe
  8
+from django.utils.functional import lazy
9 9
 from django.utils.safestring import mark_safe
  10
+from django.utils.translation import get_language, to_locale, check_for_language
10 11
 
11 12
 # format_cache is a mapping from (format_type, lang) to the format string.
12 13
 # 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):
81 82
             _format_cache[cache_key] = None
82 83
     return getattr(settings, format_type)
83 84
 
  85
+get_format_lazy = lazy(get_format, unicode, list, tuple)
  86
+
84 87
 def date_format(value, format=None, use_l10n=None):
85 88
     """
86 89
     Formats a datetime.date or datetime.datetime object using a
30  tests/regressiontests/forms/tests/fields.py
@@ -334,6 +334,17 @@ def test_datefield_3(self):
334 334
         self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '10/25/2006')
335 335
         self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '10/25/06')
336 336
 
  337
+    def test_datefield_4(self):
  338
+        # Test whitespace stripping behavior (#5714)
  339
+        f = DateField()
  340
+        self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 10/25/2006 '))
  341
+        self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 10/25/06 '))
  342
+        self.assertEqual(datetime.date(2006, 10, 25), f.clean(' Oct 25   2006 '))
  343
+        self.assertEqual(datetime.date(2006, 10, 25), f.clean(' October  25 2006 '))
  344
+        self.assertEqual(datetime.date(2006, 10, 25), f.clean(' October 25, 2006 '))
  345
+        self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 25 October 2006 '))
  346
+        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '   ')
  347
+
337 348
     # TimeField ###################################################################
338 349
 
339 350
     def test_timefield_1(self):
@@ -353,6 +364,13 @@ def test_timefield_2(self):
353 364
         self.assertEqual(datetime.time(16, 25), f.clean('4:25 PM'))
354 365
         self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, '14:30:45')
355 366
 
  367
+    def test_timefield_3(self):
  368
+        f = TimeField()
  369
+        # Test whitespace stripping behavior (#5714)
  370
+        self.assertEqual(datetime.time(14, 25), f.clean(' 14:25 '))
  371
+        self.assertEqual(datetime.time(14, 25, 59), f.clean(' 14:25:59 '))
  372
+        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, '   ')
  373
+
356 374
     # DateTimeField ###############################################################
357 375
 
358 376
     def test_datetimefield_1(self):
@@ -392,6 +410,18 @@ def test_datetimefield_3(self):
392 410
         self.assertEqual(None, f.clean(''))
393 411
         self.assertEqual('None', repr(f.clean('')))
394 412
 
  413
+    def test_datetimefield_4(self):
  414
+        f = DateTimeField()
  415
+        # Test whitespace stripping behavior (#5714)
  416
+        self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 2006-10-25   14:30:45 '))
  417
+        self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 2006-10-25 '))
  418
+        self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 10/25/2006 14:30:45 '))
  419
+        self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(' 10/25/2006 14:30 '))
  420
+        self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 10/25/2006 '))
  421
+        self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 10/25/06 14:30:45 '))
  422
+        self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 10/25/06 '))
  423
+        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date/time.']", f.clean, '   ')
  424
+
395 425
     # RegexField ##################################################################
396 426
 
397 427
     def test_regexfield_1(self):

0 notes on commit da3aa22

Please sign in to comment.
Something went wrong with that request. Please try again.