Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #12467 -- Made the model data validation for `DateField` and `D…

…ateTimeField` more useful by actually telling what was the value that failed. Also did a bit of PEP8 cleanup in the area. Thanks to knutin for the report, to raulcd for the initial patch and to charettes for the review.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16966 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 3b22c683438e59ebe27a2d21ebd6d33fbf1644bd 1 parent 9a5262b
Julien Phalip authored October 13, 2011
212  django/db/models/fields/__init__.py
@@ -22,7 +22,8 @@
22 22
 class NOT_PROVIDED:
23 23
     pass
24 24
 
25  
-# The values to use for "blank" in SelectFields. Will be appended to the start of most "choices" lists.
  25
+# The values to use for "blank" in SelectFields. Will be appended to the start
  26
+# of most "choices" lists.
26 27
 BLANK_CHOICE_DASH = [("", "---------")]
27 28
 BLANK_CHOICE_NONE = [("", "None")]
28 29
 
@@ -61,7 +62,8 @@ class Field(object):
61 62
         'invalid_choice': _(u'Value %r is not a valid choice.'),
62 63
         'null': _(u'This field cannot be null.'),
63 64
         'blank': _(u'This field cannot be blank.'),
64  
-        'unique': _(u'%(model_name)s with this %(field_label)s already exists.'),
  65
+        'unique': _(u'%(model_name)s with this %(field_label)s '
  66
+                    u'already exists.'),
65 67
     }
66 68
 
67 69
     # Generic field type description, usually overriden by subclasses
@@ -85,13 +87,15 @@ def __init__(self, verbose_name=None, name=None, primary_key=False,
85 87
         self.blank, self.null = blank, null
86 88
         # Oracle treats the empty string ('') as null, so coerce the null
87 89
         # option whenever '' is a possible value.
88  
-        if self.empty_strings_allowed and connection.features.interprets_empty_strings_as_nulls:
  90
+        if (self.empty_strings_allowed and
  91
+            connection.features.interprets_empty_strings_as_nulls):
89 92
             self.null = True
90 93
         self.rel = rel
91 94
         self.default = default
92 95
         self.editable = editable
93 96
         self.serialize = serialize
94  
-        self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
  97
+        self.unique_for_date, self.unique_for_month = (unique_for_date,
  98
+                                                       unique_for_month)
95 99
         self.unique_for_year = unique_for_year
96 100
         self._choices = choices or []
97 101
         self.help_text = help_text
@@ -99,7 +103,8 @@ def __init__(self, verbose_name=None, name=None, primary_key=False,
99 103
         self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
100 104
         self.auto_created = auto_created
101 105
 
102  
-        # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
  106
+        # Set db_index to True if the field has a relationship and doesn't
  107
+        # explicitly set db_index.
103 108
         self.db_index = db_index
104 109
 
105 110
         # Adjust the appropriate creation counter, and save our local copy.
@@ -169,13 +174,15 @@ def validate(self, value, model_instance):
169 174
         if self._choices and value:
170 175
             for option_key, option_value in self.choices:
171 176
                 if isinstance(option_value, (list, tuple)):
172  
-                    # This is an optgroup, so look inside the group for options.
  177
+                    # This is an optgroup, so look inside the group for
  178
+                    # options.
173 179
                     for optgroup_key, optgroup_value in option_value:
174 180
                         if value == optgroup_key:
175 181
                             return
176 182
                 elif value == option_key:
177 183
                     return
178  
-            raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value)
  184
+            raise exceptions.ValidationError(
  185
+                self.error_messages['invalid_choice'] % value)
179 186
 
180 187
         if value is None and not self.null:
181 188
             raise exceptions.ValidationError(self.error_messages['null'])
@@ -185,9 +192,9 @@ def validate(self, value, model_instance):
185 192
 
186 193
     def clean(self, value, model_instance):
187 194
         """
188  
-        Convert the value's type and run validation. Validation errors from to_python
189  
-        and validate are propagated. The correct value is returned if no error is
190  
-        raised.
  195
+        Convert the value's type and run validation. Validation errors
  196
+        from to_python and validate are propagated. The correct value is
  197
+        returned if no error is raised.
191 198
         """
192 199
         value = self.to_python(value)
193 200
         self.validate(value, model_instance)
@@ -205,9 +212,9 @@ def db_type(self, connection):
205 212
         #
206 213
         # A Field class can implement the get_internal_type() method to specify
207 214
         # which *preexisting* Django Field class it's most similar to -- i.e.,
208  
-        # a custom field might be represented by a TEXT column type, which is the
209  
-        # same as the TextField Django field type, which means the custom field's
210  
-        # get_internal_type() returns 'TextField'.
  215
+        # a custom field might be represented by a TEXT column type, which is
  216
+        # the same as the TextField Django field type, which means the custom
  217
+        # field's get_internal_type() returns 'TextField'.
211 218
         #
212 219
         # But the limitation of the get_internal_type() / data_types approach
213 220
         # is that it cannot handle database column types that aren't already
@@ -216,7 +223,8 @@ def db_type(self, connection):
216 223
         # exactly which wacky database column type you want to use.
217 224
         data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
218 225
         try:
219  
-            return connection.creation.data_types[self.get_internal_type()] % data
  226
+            return (connection.creation.data_types[self.get_internal_type()]
  227
+                    % data)
220 228
         except KeyError:
221 229
             return None
222 230
 
@@ -236,7 +244,8 @@ def contribute_to_class(self, cls, name):
236 244
         self.model = cls
237 245
         cls._meta.add_field(self)
238 246
         if self.choices:
239  
-            setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self))
  247
+            setattr(cls, 'get_%s_display' % self.name,
  248
+                    curry(cls._get_FIELD_display, field=self))
240 249
 
241 250
     def get_attname(self):
242 251
         return self.name
@@ -253,11 +262,15 @@ def get_internal_type(self):
253 262
         return self.__class__.__name__
254 263
 
255 264
     def pre_save(self, model_instance, add):
256  
-        "Returns field's value just before saving."
  265
+        """
  266
+        Returns field's value just before saving.
  267
+        """
257 268
         return getattr(model_instance, self.attname)
258 269
 
259 270
     def get_prep_value(self, value):
260  
-        "Perform preliminary non-db specific value checks and conversions."
  271
+        """
  272
+        Perform preliminary non-db specific value checks and conversions.
  273
+        """
261 274
         return value
262 275
 
263 276
     def get_db_prep_value(self, value, connection, prepared=False):
@@ -272,11 +285,16 @@ def get_db_prep_value(self, value, connection, prepared=False):
272 285
         return value
273 286
 
274 287
     def get_db_prep_save(self, value, connection):
275  
-        "Returns field's value prepared for saving into a database."
276  
-        return self.get_db_prep_value(value, connection=connection, prepared=False)
  288
+        """
  289
+        Returns field's value prepared for saving into a database.
  290
+        """
  291
+        return self.get_db_prep_value(value, connection=connection,
  292
+                                      prepared=False)
277 293
 
278 294
     def get_prep_lookup(self, lookup_type, value):
279  
-        "Perform preliminary non-db specific lookup checks and conversions"
  295
+        """
  296
+        Perform preliminary non-db specific lookup checks and conversions
  297
+        """
280 298
         if hasattr(value, 'prepare'):
281 299
             return value.prepare()
282 300
         if hasattr(value, '_prepare'):
@@ -296,12 +314,16 @@ def get_prep_lookup(self, lookup_type, value):
296 314
             try:
297 315
                 return int(value)
298 316
             except ValueError:
299  
-                raise ValueError("The __year lookup type requires an integer argument")
  317
+                raise ValueError("The __year lookup type requires an integer "
  318
+                                 "argument")
300 319
 
301 320
         raise TypeError("Field has invalid lookup: %s" % lookup_type)
302 321
 
303  
-    def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
304  
-        "Returns field's value prepared for database lookup."
  322
+    def get_db_prep_lookup(self, lookup_type, value, connection,
  323
+                           prepared=False):
  324
+        """
  325
+        Returns field's value prepared for database lookup.
  326
+        """
305 327
         if not prepared:
306 328
             value = self.get_prep_lookup(lookup_type, value)
307 329
         if hasattr(value, 'get_compiler'):
@@ -317,12 +339,15 @@ def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
317 339
                 sql, params = value._as_sql(connection=connection)
318 340
             return QueryWrapper(('(%s)' % sql), params)
319 341
 
320  
-        if lookup_type in ('regex', 'iregex', 'month', 'day', 'week_day', 'search'):
  342
+        if lookup_type in ('regex', 'iregex', 'month', 'day', 'week_day',
  343
+                           'search'):
321 344
             return [value]
322 345
         elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
323  
-            return [self.get_db_prep_value(value, connection=connection, prepared=prepared)]
  346
+            return [self.get_db_prep_value(value, connection=connection,
  347
+                                           prepared=prepared)]
324 348
         elif lookup_type in ('range', 'in'):
325  
-            return [self.get_db_prep_value(v, connection=connection, prepared=prepared) for v in value]
  349
+            return [self.get_db_prep_value(v, connection=connection,
  350
+                                           prepared=prepared) for v in value]
326 351
         elif lookup_type in ('contains', 'icontains'):
327 352
             return ["%%%s%%" % connection.ops.prep_for_like_query(value)]
328 353
         elif lookup_type == 'iexact':
@@ -340,16 +365,21 @@ def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
340 365
                 return connection.ops.year_lookup_bounds(value)
341 366
 
342 367
     def has_default(self):
343  
-        "Returns a boolean of whether this field has a default value."
  368
+        """
  369
+        Returns a boolean of whether this field has a default value.
  370
+        """
344 371
         return self.default is not NOT_PROVIDED
345 372
 
346 373
     def get_default(self):
347  
-        "Returns the default value for this field."
  374
+        """
  375
+        Returns the default value for this field.
  376
+        """
348 377
         if self.has_default():
349 378
             if callable(self.default):
350 379
                 return self.default()
351 380
             return force_unicode(self.default, strings_only=True)
352  
-        if not self.empty_strings_allowed or (self.null and not connection.features.interprets_empty_strings_as_nulls):
  381
+        if (not self.empty_strings_allowed or (self.null and
  382
+                   not connection.features.interprets_empty_strings_as_nulls)):
353 383
             return None
354 384
         return ""
355 385
 
@@ -364,16 +394,24 @@ def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
364 394
             return first_choice + list(self.choices)
365 395
         rel_model = self.rel.to
366 396
         if hasattr(self.rel, 'get_related_field'):
367  
-            lst = [(getattr(x, self.rel.get_related_field().attname), smart_unicode(x)) for x in rel_model._default_manager.complex_filter(self.rel.limit_choices_to)]
  397
+            lst = [(getattr(x, self.rel.get_related_field().attname),
  398
+                        smart_unicode(x))
  399
+                   for x in rel_model._default_manager.complex_filter(
  400
+                       self.rel.limit_choices_to)]
368 401
         else:
369  
-            lst = [(x._get_pk_val(), smart_unicode(x)) for x in rel_model._default_manager.complex_filter(self.rel.limit_choices_to)]
  402
+            lst = [(x._get_pk_val(), smart_unicode(x))
  403
+                   for x in rel_model._default_manager.complex_filter(
  404
+                       self.rel.limit_choices_to)]
370 405
         return first_choice + lst
371 406
 
372 407
     def get_choices_default(self):
373 408
         return self.get_choices()
374 409
 
375  
-    def get_flatchoices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
376  
-        "Returns flattened choices with a default blank choice included."
  410
+    def get_flatchoices(self, include_blank=True,
  411
+                        blank_choice=BLANK_CHOICE_DASH):
  412
+        """
  413
+        Returns flattened choices with a default blank choice included.
  414
+        """
377 415
         first_choice = include_blank and blank_choice or []
378 416
         return first_choice + list(self.flatchoices)
379 417
 
@@ -416,8 +454,12 @@ def save_form_data(self, instance, data):
416 454
         setattr(instance, self.name, data)
417 455
 
418 456
     def formfield(self, form_class=forms.CharField, **kwargs):
419  
-        "Returns a django.forms.Field instance for this database Field."
420  
-        defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
  457
+        """
  458
+        Returns a django.forms.Field instance for this database Field.
  459
+        """
  460
+        defaults = {'required': not self.blank,
  461
+                    'label': capfirst(self.verbose_name),
  462
+                    'help_text': self.help_text}
421 463
         if self.has_default():
422 464
             if callable(self.default):
423 465
                 defaults['initial'] = self.default
@@ -426,7 +468,8 @@ def formfield(self, form_class=forms.CharField, **kwargs):
426 468
                 defaults['initial'] = self.get_default()
427 469
         if self.choices:
428 470
             # Fields with choices get special treatment.
429  
-            include_blank = self.blank or not (self.has_default() or 'initial' in kwargs)
  471
+            include_blank = (self.blank or
  472
+                             not (self.has_default() or 'initial' in kwargs))
430 473
             defaults['choices'] = self.get_choices(include_blank=include_blank)
431 474
             defaults['coerce'] = self.to_python
432 475
             if self.null:
@@ -444,7 +487,9 @@ def formfield(self, form_class=forms.CharField, **kwargs):
444 487
         return form_class(**defaults)
445 488
 
446 489
     def value_from_object(self, obj):
447  
-        "Returns the value of this field in the given model instance."
  490
+        """
  491
+        Returns the value of this field in the given model instance.
  492
+        """
448 493
         return getattr(obj, self.attname)
449 494
 
450 495
     def __repr__(self):
@@ -465,7 +510,8 @@ class AutoField(Field):
465 510
         'invalid': _(u"'%s' value must be an integer."),
466 511
     }
467 512
     def __init__(self, *args, **kwargs):
468  
-        assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
  513
+        assert (kwargs.get('primary_key', False) is True,
  514
+                "%ss must have primary_key=True." % self.__class__.__name__)
469 515
         kwargs['blank'] = True
470 516
         Field.__init__(self, *args, **kwargs)
471 517
 
@@ -490,7 +536,8 @@ def get_prep_value(self, value):
490 536
         return int(value)
491 537
 
492 538
     def contribute_to_class(self, cls, name):
493  
-        assert not cls._meta.has_auto_field, "A model can't have more than one AutoField."
  539
+        assert (not cls._meta.has_auto_field,
  540
+                "A model can't have more than one AutoField.")
494 541
         super(AutoField, self).contribute_to_class(cls, name)
495 542
         cls._meta.has_auto_field = True
496 543
         cls._meta.auto_field = self
@@ -543,8 +590,10 @@ def formfield(self, **kwargs):
543 590
         # Unlike most fields, BooleanField figures out include_blank from
544 591
         # self.null instead of self.blank.
545 592
         if self.choices:
546  
-            include_blank = self.null or not (self.has_default() or 'initial' in kwargs)
547  
-            defaults = {'choices': self.get_choices(include_blank=include_blank)}
  593
+            include_blank = (self.null or
  594
+                             not (self.has_default() or 'initial' in kwargs))
  595
+            defaults = {'choices': self.get_choices(
  596
+                                       include_blank=include_blank)}
548 597
         else:
549 598
             defaults = {'form_class': forms.BooleanField}
550 599
         defaults.update(kwargs)
@@ -597,12 +646,16 @@ class DateField(Field):
597 646
 
598 647
     empty_strings_allowed = False
599 648
     default_error_messages = {
600  
-        'invalid': _('Enter a valid date in YYYY-MM-DD format.'),
601  
-        'invalid_date': _('Invalid date: %s'),
  649
+        'invalid': _(u"'%s' value has an invalid date format. It must be "
  650
+                     u"in YYYY-MM-DD format."),
  651
+        'invalid_date': _(u"'%s' value has the correct format (YYYY-MM-DD) "
  652
+                          u"but it is an invalid date."),
602 653
     }
603  
-    def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
  654
+    def __init__(self, verbose_name=None, name=None, auto_now=False,
  655
+                 auto_now_add=False, **kwargs):
604 656
         self.auto_now, self.auto_now_add = auto_now, auto_now_add
605  
-        #HACKs : auto_now_add/auto_now should be done as a default or a pre_save.
  657
+        # HACKs : auto_now_add/auto_now should be done as a default or a
  658
+        # pre_save.
606 659
         if auto_now or auto_now_add:
607 660
             kwargs['editable'] = False
608 661
             kwargs['blank'] = True
@@ -620,7 +673,8 @@ def to_python(self, value):
620 673
             return value
621 674
 
622 675
         if not ansi_date_re.search(value):
623  
-            raise exceptions.ValidationError(self.error_messages['invalid'])
  676
+            msg = self.error_messages['invalid'] % str(value)
  677
+            raise exceptions.ValidationError(msg)
624 678
         # Now that we have the date string in YYYY-MM-DD format, check to make
625 679
         # sure it's a valid date.
626 680
         # We could use time.strptime here and catch errors, but datetime.date
@@ -629,7 +683,7 @@ def to_python(self, value):
629 683
         try:
630 684
             return datetime.date(year, month, day)
631 685
         except ValueError, e:
632  
-            msg = self.error_messages['invalid_date'] % _(str(e))
  686
+            msg = self.error_messages['invalid_date'] % str(value)
633 687
             raise exceptions.ValidationError(msg)
634 688
 
635 689
     def pre_save(self, model_instance, add):
@@ -644,9 +698,11 @@ def contribute_to_class(self, cls, name):
644 698
         super(DateField,self).contribute_to_class(cls, name)
645 699
         if not self.null:
646 700
             setattr(cls, 'get_next_by_%s' % self.name,
647  
-                curry(cls._get_next_or_previous_by_FIELD, field=self, is_next=True))
  701
+                curry(cls._get_next_or_previous_by_FIELD, field=self,
  702
+                      is_next=True))
648 703
             setattr(cls, 'get_previous_by_%s' % self.name,
649  
-                curry(cls._get_next_or_previous_by_FIELD, field=self, is_next=False))
  704
+                curry(cls._get_next_or_previous_by_FIELD, field=self,
  705
+                      is_next=False))
650 706
 
651 707
     def get_prep_lookup(self, lookup_type, value):
652 708
         # For "__month", "__day", and "__week_day" lookups, convert the value
@@ -679,7 +735,9 @@ def formfield(self, **kwargs):
679 735
 
680 736
 class DateTimeField(DateField):
681 737
     default_error_messages = {
682  
-        'invalid': _(u'Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.'),
  738
+        'invalid': _(u"'%s' value either has an invalid valid format (The "
  739
+                     u"format must be YYYY-MM-DD HH:MM[:ss[.uuuuuu]]) or is "
  740
+                     u"an invalid date/time."),
683 741
     }
684 742
     description = _("Date (with time)")
685 743
 
@@ -702,24 +760,26 @@ def to_python(self, value):
702 760
                 value, usecs = value.split('.')
703 761
                 usecs = int(usecs)
704 762
             except ValueError:
705  
-                raise exceptions.ValidationError(self.error_messages['invalid'])
  763
+                raise exceptions.ValidationError(
  764
+                    self.error_messages['invalid'] % str(value))
706 765
         else:
707 766
             usecs = 0
708 767
         kwargs = {'microsecond': usecs}
709 768
         try: # Seconds are optional, so try converting seconds first.
710  
-            return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6],
711  
-                                     **kwargs)
  769
+            return datetime.datetime(
  770
+                *time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6], **kwargs)
712 771
 
713 772
         except ValueError:
714 773
             try: # Try without seconds.
715  
-                return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5],
716  
-                                         **kwargs)
  774
+                return datetime.datetime(
  775
+                    *time.strptime(value, '%Y-%m-%d %H:%M')[:5], **kwargs)
717 776
             except ValueError: # Try without hour/minutes/seconds.
718 777
                 try:
719  
-                    return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3],
720  
-                                             **kwargs)
  778
+                    return datetime.datetime(
  779
+                        *time.strptime(value, '%Y-%m-%d')[:3], **kwargs)
721 780
                 except ValueError:
722  
-                    raise exceptions.ValidationError(self.error_messages['invalid'])
  781
+                    raise exceptions.ValidationError(
  782
+                        self.error_messages['invalid'] % str(value))
723 783
 
724 784
     def pre_save(self, model_instance, add):
725 785
         if self.auto_now or (self.auto_now_add and add):
@@ -759,7 +819,8 @@ class DecimalField(Field):
759 819
     }
760 820
     description = _("Decimal number")
761 821
 
762  
-    def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
  822
+    def __init__(self, verbose_name=None, name=None, max_digits=None,
  823
+                 decimal_places=None, **kwargs):
763 824
         self.max_digits, self.decimal_places = max_digits, decimal_places
764 825
         Field.__init__(self, verbose_name, name, **kwargs)
765 826
 
@@ -820,7 +881,8 @@ def __init__(self, *args, **kwargs):
820 881
         CharField.__init__(self, *args, **kwargs)
821 882
 
822 883
     def formfield(self, **kwargs):
823  
-        # As with CharField, this will cause email validation to be performed twice
  884
+        # As with CharField, this will cause email validation to be performed
  885
+        # twice.
824 886
         defaults = {
825 887
             'form_class': forms.EmailField,
826 888
         }
@@ -830,7 +892,8 @@ def formfield(self, **kwargs):
830 892
 class FilePathField(Field):
831 893
     description = _("File path")
832 894
 
833  
-    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
  895
+    def __init__(self, verbose_name=None, name=None, path='', match=None,
  896
+                 recursive=False, **kwargs):
834 897
         self.path, self.match, self.recursive = path, match, recursive
835 898
         kwargs['max_length'] = kwargs.get('max_length', 100)
836 899
         Field.__init__(self, verbose_name, name, **kwargs)
@@ -890,9 +953,9 @@ def get_prep_value(self, value):
890 953
         return int(value)
891 954
 
892 955
     def get_prep_lookup(self, lookup_type, value):
893  
-        if (lookup_type == 'gte' or lookup_type == 'lt') \
894  
-           and isinstance(value, float):
895  
-                value = math.ceil(value)
  956
+        if ((lookup_type == 'gte' or lookup_type == 'lt')
  957
+            and isinstance(value, float)):
  958
+            value = math.ceil(value)
896 959
         return super(IntegerField, self).get_prep_lookup(lookup_type, value)
897 960
 
898 961
     def get_internal_type(self):
@@ -1019,7 +1082,8 @@ def get_prep_lookup(self, lookup_type, value):
1019 1082
         # constructing the list.
1020 1083
         if value in ('1', '0'):
1021 1084
             value = bool(int(value))
1022  
-        return super(NullBooleanField, self).get_prep_lookup(lookup_type, value)
  1085
+        return super(NullBooleanField, self).get_prep_lookup(lookup_type,
  1086
+                                                             value)
1023 1087
 
1024 1088
     def get_prep_value(self, value):
1025 1089
         if value is None:
@@ -1102,7 +1166,8 @@ class TimeField(Field):
1102 1166
     default_error_messages = {
1103 1167
         'invalid': _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.'),
1104 1168
     }
1105  
-    def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
  1169
+    def __init__(self, verbose_name=None, name=None, auto_now=False,
  1170
+                 auto_now_add=False, **kwargs):
1106 1171
         self.auto_now, self.auto_now_add = auto_now, auto_now_add
1107 1172
         if auto_now or auto_now_add:
1108 1173
             kwargs['editable'] = False
@@ -1130,7 +1195,8 @@ def to_python(self, value):
1130 1195
                 value, usecs = value.split('.')
1131 1196
                 usecs = int(usecs)
1132 1197
             except ValueError:
1133  
-                raise exceptions.ValidationError(self.error_messages['invalid'])
  1198
+                raise exceptions.ValidationError(
  1199
+                    self.error_messages['invalid'])
1134 1200
         else:
1135 1201
             usecs = 0
1136 1202
         kwargs = {'microsecond': usecs}
@@ -1143,7 +1209,8 @@ def to_python(self, value):
1143 1209
                 return datetime.time(*time.strptime(value, '%H:%M')[3:5],
1144 1210
                                          **kwargs)
1145 1211
             except ValueError:
1146  
-                raise exceptions.ValidationError(self.error_messages['invalid'])
  1212
+                raise exceptions.ValidationError(
  1213
+                    self.error_messages['invalid'])
1147 1214
 
1148 1215
     def pre_save(self, model_instance, add):
1149 1216
         if self.auto_now or (self.auto_now_add and add):
@@ -1178,13 +1245,16 @@ def formfield(self, **kwargs):
1178 1245
 class URLField(CharField):
1179 1246
     description = _("URL")
1180 1247
 
1181  
-    def __init__(self, verbose_name=None, name=None, verify_exists=False, **kwargs):
  1248
+    def __init__(self, verbose_name=None, name=None, verify_exists=False,
  1249
+                 **kwargs):
1182 1250
         kwargs['max_length'] = kwargs.get('max_length', 200)
1183 1251
         CharField.__init__(self, verbose_name, name, **kwargs)
1184  
-        self.validators.append(validators.URLValidator(verify_exists=verify_exists))
  1252
+        self.validators.append(
  1253
+            validators.URLValidator(verify_exists=verify_exists))
1185 1254
 
1186 1255
     def formfield(self, **kwargs):
1187  
-        # As with CharField, this will cause URL validation to be performed twice
  1256
+        # As with CharField, this will cause URL validation to be performed
  1257
+        # twice.
1188 1258
         defaults = {
1189 1259
             'form_class': forms.URLField,
1190 1260
         }
56  tests/modeltests/validation/test_error_messages.py
@@ -55,3 +55,59 @@ def test_null_boolean_field_raises_error_message(self):
55 55
         except ValidationError, e:
56 56
             self.assertEqual(e.messages,
57 57
                         [u"'foo' value must be either None, True or False."])
  58
+
  59
+    def test_date_field_raises_error_message(self):
  60
+        f = models.DateField()
  61
+        self.assertRaises(ValidationError, f.clean, 'foo', None)
  62
+        try:
  63
+            f.clean('foo', None)
  64
+        except ValidationError, e:
  65
+            self.assertEqual(e.messages, [
  66
+                u"'foo' value has an invalid date format. "
  67
+                u"It must be in YYYY-MM-DD format."])
  68
+
  69
+        self.assertRaises(ValidationError, f.clean, 'aaaa-10-10', None)
  70
+        try:
  71
+            f.clean('aaaa-10-10', None)
  72
+        except ValidationError, e:
  73
+            self.assertEqual(e.messages, [
  74
+                u"'aaaa-10-10' value has an invalid date format. "
  75
+                u"It must be in YYYY-MM-DD format."])
  76
+
  77
+        self.assertRaises(ValidationError, f.clean, '2011-13-10', None)
  78
+        try:
  79
+            f.clean('2011-13-10', None)
  80
+        except ValidationError, e:
  81
+            self.assertEqual(e.messages, [
  82
+                u"'2011-13-10' value has the correct format (YYYY-MM-DD) "
  83
+                u"but it is an invalid date."])
  84
+
  85
+        self.assertRaises(ValidationError, f.clean, '2011-10-32', None)
  86
+        try:
  87
+            f.clean('2011-10-32', None)
  88
+        except ValidationError, e:
  89
+            self.assertEqual(e.messages, [
  90
+                u"'2011-10-32' value has the correct format (YYYY-MM-DD) "
  91
+                u"but it is an invalid date."])
  92
+
  93
+    def test_datetime_field_raises_error_message(self):
  94
+        f = models.DateTimeField()
  95
+        # Wrong format
  96
+        self.assertRaises(ValidationError, f.clean, 'foo', None)
  97
+        try:
  98
+            f.clean('foo', None)
  99
+        except ValidationError, e:
  100
+            self.assertEqual(e.messages, [
  101
+                u"'foo' value either has an invalid valid format "
  102
+                u"(The format must be YYYY-MM-DD HH:MM[:ss[.uuuuuu]]) "
  103
+                u"or is an invalid date/time."])
  104
+        self.assertRaises(ValidationError, f.clean,
  105
+                          '2011-10-32 10:10', None)
  106
+        # Correct format but invalid date/time
  107
+        try:
  108
+            f.clean('2011-10-32 10:10', None)
  109
+        except ValidationError, e:
  110
+            self.assertEqual(e.messages, [
  111
+                u"'2011-10-32 10:10' value either has an invalid valid format "
  112
+                u"(The format must be YYYY-MM-DD HH:MM[:ss[.uuuuuu]]) "
  113
+                u"or is an invalid date/time."])
2  tests/modeltests/validation/tests.py
@@ -12,7 +12,7 @@
12 12
 from modeltests.validation.test_unique import (GetUniqueCheckTests,
13 13
     PerformUniqueChecksTest)
14 14
 from modeltests.validation.test_custom_messages import CustomMessagesTest
15  
-
  15
+from modeltests.validation.test_error_messages import ValidationMessagesTest
16 16
 
17 17
 class BaseModelValidationTests(ValidationTestCase):
18 18
 

0 notes on commit 3b22c68

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