Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #19997 -- Added custom EMPTY_VALUES to form fields

Thanks Loic Bistuer for the report and the patch.
  • Loading branch information...
commit 4cccb85e292fea01b3459cd97d751ed35179a7b7 1 parent 25ce177
Claude Paroz authored March 07, 2013
1  AUTHORS
@@ -98,6 +98,7 @@ answer newbie questions, and generally made Django that much better:
98 98
     Natalia Bidart <nataliabidart@gmail.com>
99 99
     Mark Biggers <biggers@utsl.com>
100 100
     Paul Bissex <http://e-scribe.com/>
  101
+    Loic Bistuer <loic.bistuer@sixmedia.com>
101 102
     Simon Blanchard
102 103
     Craig Blaszczyk <masterjakul@gmail.com>
103 104
     David Blewett <david@dawninglight.net>
45  django/forms/fields.py
@@ -53,6 +53,7 @@ class Field(object):
53 53
         'required': _('This field is required.'),
54 54
         'invalid': _('Enter a valid value.'),
55 55
     }
  56
+    empty_values = list(validators.EMPTY_VALUES)
56 57
 
57 58
     # Tracks each time a Field instance is created. Used to retain order.
58 59
     creation_counter = 0
@@ -125,11 +126,11 @@ def to_python(self, value):
125 126
         return value
126 127
 
127 128
     def validate(self, value):
128  
-        if value in validators.EMPTY_VALUES and self.required:
  129
+        if value in self.empty_values and self.required:
129 130
             raise ValidationError(self.error_messages['required'])
130 131
 
131 132
     def run_validators(self, value):
132  
-        if value in validators.EMPTY_VALUES:
  133
+        if value in self.empty_values:
133 134
             return
134 135
         errors = []
135 136
         for v in self.validators:
@@ -210,7 +211,7 @@ def __init__(self, max_length=None, min_length=None, *args, **kwargs):
210 211
 
211 212
     def to_python(self, value):
212 213
         "Returns a Unicode object."
213  
-        if value in validators.EMPTY_VALUES:
  214
+        if value in self.empty_values:
214 215
             return ''
215 216
         return smart_text(value)
216 217
 
@@ -244,7 +245,7 @@ def to_python(self, value):
244 245
         of int(). Returns None for empty values.
245 246
         """
246 247
         value = super(IntegerField, self).to_python(value)
247  
-        if value in validators.EMPTY_VALUES:
  248
+        if value in self.empty_values:
248 249
             return None
249 250
         if self.localize:
250 251
             value = formats.sanitize_separators(value)
@@ -275,7 +276,7 @@ def to_python(self, value):
275 276
         of float(). Returns None for empty values.
276 277
         """
277 278
         value = super(IntegerField, self).to_python(value)
278  
-        if value in validators.EMPTY_VALUES:
  279
+        if value in self.empty_values:
279 280
             return None
280 281
         if self.localize:
281 282
             value = formats.sanitize_separators(value)
@@ -311,7 +312,7 @@ def to_python(self, value):
311 312
         than max_digits in the number, and no more than decimal_places digits
312 313
         after the decimal point.
313 314
         """
314  
-        if value in validators.EMPTY_VALUES:
  315
+        if value in self.empty_values:
315 316
             return None
316 317
         if self.localize:
317 318
             value = formats.sanitize_separators(value)
@@ -324,7 +325,7 @@ def to_python(self, value):
324 325
 
325 326
     def validate(self, value):
326 327
         super(DecimalField, self).validate(value)
327  
-        if value in validators.EMPTY_VALUES:
  328
+        if value in self.empty_values:
328 329
             return
329 330
         # Check for NaN, Inf and -Inf values. We can't compare directly for NaN,
330 331
         # since it is never equal to itself. However, NaN is the only value that
@@ -401,7 +402,7 @@ def to_python(self, value):
401 402
         Validates that the input can be converted to a date. Returns a Python
402 403
         datetime.date object.
403 404
         """
404  
-        if value in validators.EMPTY_VALUES:
  405
+        if value in self.empty_values:
405 406
             return None
406 407
         if isinstance(value, datetime.datetime):
407 408
             return value.date()
@@ -425,7 +426,7 @@ def to_python(self, value):
425 426
         Validates that the input can be converted to a time. Returns a Python
426 427
         datetime.time object.
427 428
         """
428  
-        if value in validators.EMPTY_VALUES:
  429
+        if value in self.empty_values:
429 430
             return None
430 431
         if isinstance(value, datetime.time):
431 432
             return value
@@ -451,7 +452,7 @@ def to_python(self, value):
451 452
         Validates that the input can be converted to a datetime. Returns a
452 453
         Python datetime.datetime object.
453 454
         """
454  
-        if value in validators.EMPTY_VALUES:
  455
+        if value in self.empty_values:
455 456
             return None
456 457
         if isinstance(value, datetime.datetime):
457 458
             return from_current_timezone(value)
@@ -463,7 +464,7 @@ def to_python(self, value):
463 464
             # components: date and time.
464 465
             if len(value) != 2:
465 466
                 raise ValidationError(self.error_messages['invalid'])
466  
-            if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES:
  467
+            if value[0] in self.empty_values and value[1] in self.empty_values:
467 468
                 return None
468 469
             value = '%s %s' % tuple(value)
469 470
         result = super(DateTimeField, self).to_python(value)
@@ -531,7 +532,7 @@ def __init__(self, *args, **kwargs):
531 532
         super(FileField, self).__init__(*args, **kwargs)
532 533
 
533 534
     def to_python(self, data):
534  
-        if data in validators.EMPTY_VALUES:
  535
+        if data in self.empty_values:
535 536
             return None
536 537
 
537 538
         # UploadedFile objects should have name and size attributes.
@@ -562,7 +563,7 @@ def clean(self, data, initial=None):
562 563
                 return False
563 564
             # If the field is required, clearing is not possible (the widget
564 565
             # shouldn't return False data in that case anyway). False is not
565  
-            # in validators.EMPTY_VALUES; if a False value makes it this far
  566
+            # in self.empty_value; if a False value makes it this far
566 567
             # it should be validated from here on out as None (so it will be
567 568
             # caught by the required check).
568 569
             data = None
@@ -763,7 +764,7 @@ def _set_choices(self, value):
763 764
 
764 765
     def to_python(self, value):
765 766
         "Returns a Unicode object."
766  
-        if value in validators.EMPTY_VALUES:
  767
+        if value in self.empty_values:
767 768
             return ''
768 769
         return smart_text(value)
769 770
 
@@ -801,7 +802,7 @@ def to_python(self, value):
801 802
         """
802 803
         value = super(TypedChoiceField, self).to_python(value)
803 804
         super(TypedChoiceField, self).validate(value)
804  
-        if value == self.empty_value or value in validators.EMPTY_VALUES:
  805
+        if value == self.empty_value or value in self.empty_values:
805 806
             return self.empty_value
806 807
         try:
807 808
             value = self.coerce(value)
@@ -864,7 +865,7 @@ def to_python(self, value):
864 865
         """
865 866
         value = super(TypedMultipleChoiceField, self).to_python(value)
866 867
         super(TypedMultipleChoiceField, self).validate(value)
867  
-        if value == self.empty_value or value in validators.EMPTY_VALUES:
  868
+        if value == self.empty_value or value in self.empty_values:
868 869
             return self.empty_value
869 870
         new_value = []
870 871
         for choice in value:
@@ -945,7 +946,7 @@ def clean(self, value):
945 946
         clean_data = []
946 947
         errors = ErrorList()
947 948
         if not value or isinstance(value, (list, tuple)):
948  
-            if not value or not [v for v in value if v not in validators.EMPTY_VALUES]:
  949
+            if not value or not [v for v in value if v not in self.empty_values]:
949 950
                 if self.required:
950 951
                     raise ValidationError(self.error_messages['required'])
951 952
                 else:
@@ -957,7 +958,7 @@ def clean(self, value):
957 958
                 field_value = value[i]
958 959
             except IndexError:
959 960
                 field_value = None
960  
-            if self.required and field_value in validators.EMPTY_VALUES:
  961
+            if self.required and field_value in self.empty_values:
961 962
                 raise ValidationError(self.error_messages['required'])
962 963
             try:
963 964
                 clean_data.append(field.clean(field_value))
@@ -1071,9 +1072,9 @@ def compress(self, data_list):
1071 1072
         if data_list:
1072 1073
             # Raise a validation error if time or date is empty
1073 1074
             # (possible if SplitDateTimeField has required=False).
1074  
-            if data_list[0] in validators.EMPTY_VALUES:
  1075
+            if data_list[0] in self.empty_values:
1075 1076
                 raise ValidationError(self.error_messages['invalid_date'])
1076  
-            if data_list[1] in validators.EMPTY_VALUES:
  1077
+            if data_list[1] in self.empty_values:
1077 1078
                 raise ValidationError(self.error_messages['invalid_time'])
1078 1079
             result = datetime.datetime.combine(*data_list)
1079 1080
             return from_current_timezone(result)
@@ -1087,7 +1088,7 @@ class IPAddressField(CharField):
1087 1088
     default_validators = [validators.validate_ipv4_address]
1088 1089
 
1089 1090
     def to_python(self, value):
1090  
-        if value in EMPTY_VALUES:
  1091
+        if value in self.empty_values:
1091 1092
             return ''
1092 1093
         return value.strip()
1093 1094
 
@@ -1103,7 +1104,7 @@ def __init__(self, protocol='both', unpack_ipv4=False, *args, **kwargs):
1103 1104
         super(GenericIPAddressField, self).__init__(*args, **kwargs)
1104 1105
 
1105 1106
     def to_python(self, value):
1106  
-        if value in validators.EMPTY_VALUES:
  1107
+        if value in self.empty_values:
1107 1108
             return ''
1108 1109
         value = value.strip()
1109 1110
         if value and ':' in value:
7  django/forms/models.py
@@ -6,7 +6,6 @@
6 6
 from __future__ import absolute_import, unicode_literals
7 7
 
8 8
 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError
9  
-from django.core.validators import EMPTY_VALUES
10 9
 from django.forms.fields import Field, ChoiceField
11 10
 from django.forms.forms import BaseForm, get_declared_fields
12 11
 from django.forms.formsets import BaseFormSet, formset_factory
@@ -301,7 +300,7 @@ def _get_validation_exclusions(self):
301 300
             else:
302 301
                 form_field = self.fields[field]
303 302
                 field_value = self.cleaned_data.get(field, None)
304  
-                if not f.blank and not form_field.required and field_value in EMPTY_VALUES:
  303
+                if not f.blank and not form_field.required and field_value in form_field.empty_values:
305 304
                     exclude.append(f.name)
306 305
         return exclude
307 306
 
@@ -880,7 +879,7 @@ def __init__(self, parent_instance, *args, **kwargs):
880 879
         super(InlineForeignKeyField, self).__init__(*args, **kwargs)
881 880
 
882 881
     def clean(self, value):
883  
-        if value in EMPTY_VALUES:
  882
+        if value in self.empty_values:
884 883
             if self.pk_field:
885 884
                 return None
886 885
             # if there is no value act as we did before.
@@ -1000,7 +999,7 @@ def prepare_value(self, value):
1000 999
         return super(ModelChoiceField, self).prepare_value(value)
1001 1000
 
1002 1001
     def to_python(self, value):
1003  
-        if value in EMPTY_VALUES:
  1002
+        if value in self.empty_values:
1004 1003
             return None
1005 1004
         try:
1006 1005
             key = self.to_field_name or 'pk'
5  django/test/testcases.py
@@ -27,7 +27,6 @@
27 27
 from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer,
28 28
     WSGIServerException)
29 29
 from django.core.urlresolvers import clear_url_caches
30  
-from django.core.validators import EMPTY_VALUES
31 30
 from django.db import connection, connections, DEFAULT_DB_ALIAS, transaction
32 31
 from django.forms.fields import CharField
33 32
 from django.http import QueryDict
@@ -322,7 +321,7 @@ def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None,
322 321
                     raised error messages.
323 322
             field_args: the args passed to instantiate the field
324 323
             field_kwargs: the kwargs passed to instantiate the field
325  
-            empty_value: the expected clean output for inputs in EMPTY_VALUES
  324
+            empty_value: the expected clean output for inputs in empty_values
326 325
 
327 326
         """
328 327
         if field_args is None:
@@ -347,7 +346,7 @@ def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None,
347 346
             self.assertEqual(context_manager.exception.messages, errors)
348 347
         # test required inputs
349 348
         error_required = [force_text(required.error_messages['required'])]
350  
-        for e in EMPTY_VALUES:
  349
+        for e in required.empty_values:
351 350
             with self.assertRaises(ValidationError) as context_manager:
352 351
                 required.clean(e)
353 352
             self.assertEqual(context_manager.exception.messages,
2  docs/topics/testing/overview.txt
@@ -1480,7 +1480,7 @@ your test suite.
1480 1480
         error messages.
1481 1481
     :param field_args: the args passed to instantiate the field.
1482 1482
     :param field_kwargs: the kwargs passed to instantiate the field.
1483  
-    :param empty_value: the expected clean output for inputs in ``EMPTY_VALUES``.
  1483
+    :param empty_value: the expected clean output for inputs in ``empty_values``.
1484 1484
 
1485 1485
     For example, the following code tests that an ``EmailField`` accepts
1486 1486
     "a@a.com" as a valid email address, but rejects "aaa" with a reasonable
20  tests/forms_tests/tests/forms.py
@@ -1797,3 +1797,23 @@ class NameForm(Form):
1797 1797
         form = NameForm(data={'name' : ['fname', 'lname']})
1798 1798
         self.assertTrue(form.is_valid())
1799 1799
         self.assertEqual(form.cleaned_data, {'name' : 'fname lname'})
  1800
+
  1801
+    def test_custom_empty_values(self):
  1802
+        """
  1803
+        Test that form fields can customize what is considered as an empty value
  1804
+        for themselves (#19997).
  1805
+        """
  1806
+        class CustomJSONField(CharField):
  1807
+            empty_values = [None, '']
  1808
+            def to_python(self, value):
  1809
+                # Fake json.loads
  1810
+                if value == '{}':
  1811
+                    return {}
  1812
+                return super(CustomJSONField, self).to_python(value)
  1813
+
  1814
+        class JSONForm(forms.Form):
  1815
+            json = CustomJSONField()
  1816
+
  1817
+        form = JSONForm(data={'json': '{}'});
  1818
+        form.full_clean()
  1819
+        self.assertEqual(form.cleaned_data, {'json' : {}})

0 notes on commit 4cccb85

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