aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2014-09-23 14:30:17 +0100
committerTom Christie2014-09-23 14:30:17 +0100
commit0404f09a7e69f533038d47ca25caad90c0c2659f (patch)
treeeae2ceb609d868d02ba84c09d457b3ae6691f33b
parentf22d0afc3dfc7478e084d1d6ed6b53f71641dec6 (diff)
downloaddjango-rest-framework-0404f09a7e69f533038d47ca25caad90c0c2659f.tar.bz2
NullBooleanField
-rw-r--r--rest_framework/fields.py37
-rw-r--r--rest_framework/serializers.py2
-rw-r--r--rest_framework/utils/field_mapping.py2
-rw-r--r--tests/test_fields.py29
-rw-r--r--tests/test_model_serializer.py2
5 files changed, 67 insertions, 5 deletions
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index f5bae734..f859658a 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -289,6 +289,10 @@ class BooleanField(Field):
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
+ def __init__(self, **kwargs):
+ assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.'
+ super(BooleanField, self).__init__(**kwargs)
+
def to_internal_value(self, data):
if data in self.TRUE_VALUES:
return True
@@ -297,7 +301,38 @@ class BooleanField(Field):
self.fail('invalid', input=data)
def to_representation(self, value):
- if value is None:
+ if value in self.TRUE_VALUES:
+ return True
+ elif value in self.FALSE_VALUES:
+ return False
+ return bool(value)
+
+
+class NullBooleanField(Field):
+ default_error_messages = {
+ 'invalid': _('`{input}` is not a valid boolean.')
+ }
+ default_empty_html = None
+ TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
+ FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
+ NULL_VALUES = set(('n', 'N', 'null', 'Null', 'NULL', '', None))
+
+ def __init__(self, **kwargs):
+ assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
+ kwargs['allow_null'] = True
+ super(NullBooleanField, self).__init__(**kwargs)
+
+ def to_internal_value(self, data):
+ if data in self.TRUE_VALUES:
+ return True
+ elif data in self.FALSE_VALUES:
+ return False
+ elif data in self.NULL_VALUES:
+ return None
+ self.fail('invalid', input=data)
+
+ def to_representation(self, value):
+ if value in self.NULL_VALUES:
return None
if value in self.TRUE_VALUES:
return True
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 949f5915..d8d72a4c 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -333,7 +333,7 @@ class ModelSerializer(Serializer):
models.FloatField: FloatField,
models.ImageField: ImageField,
models.IntegerField: IntegerField,
- models.NullBooleanField: BooleanField,
+ models.NullBooleanField: NullBooleanField,
models.PositiveIntegerField: IntegerField,
models.PositiveSmallIntegerField: IntegerField,
models.SlugField: SlugField,
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
index 1c718ccb..c208afdc 100644
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -74,7 +74,7 @@ def get_field_kwargs(field_name, model_field):
kwargs['choices'] = model_field.flatchoices
return kwargs
- if model_field.null:
+ if model_field.null and not isinstance(model_field, models.NullBooleanField):
kwargs['allow_null'] = True
if model_field.blank:
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 6bf9aed4..91f3f5db 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -124,7 +124,8 @@ class TestBooleanField(FieldValues):
False: False,
}
invalid_inputs = {
- 'foo': ['`foo` is not a valid boolean.']
+ 'foo': ['`foo` is not a valid boolean.'],
+ None: ['This field may not be null.']
}
outputs = {
'true': True,
@@ -140,6 +141,32 @@ class TestBooleanField(FieldValues):
field = fields.BooleanField()
+class TestNullBooleanField(FieldValues):
+ """
+ Valid and invalid values for `BooleanField`.
+ """
+ valid_inputs = {
+ 'true': True,
+ 'false': False,
+ 'null': None,
+ True: True,
+ False: False,
+ None: None
+ }
+ invalid_inputs = {
+ 'foo': ['`foo` is not a valid boolean.'],
+ }
+ outputs = {
+ 'true': True,
+ 'false': False,
+ 'null': None,
+ True: True,
+ False: False,
+ None: None
+ }
+ field = fields.NullBooleanField()
+
+
# String types...
class TestCharField(FieldValues):
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
index 731ed2fb..f7475024 100644
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -90,7 +90,7 @@ class TestRegularFieldMappings(TestCase):
email_field = EmailField(max_length=100)
float_field = FloatField()
integer_field = IntegerField()
- null_boolean_field = BooleanField(allow_null=True)
+ null_boolean_field = NullBooleanField()
positive_integer_field = IntegerField()
positive_small_integer_field = IntegerField()
slug_field = SlugField(max_length=100)