Skip to content

Commit fc6cbb5

Browse files
rpkilbytomchristie
authored andcommitted
Allow nullable BooleanField in Django 2.1 (#6183)
* Add tests for BooleanField when nullable * Allow nullable BooleanField in Django 2.1 * Drop 'BooleanField.allow_null' check * Remove conflicting false/null values
1 parent 5f1f2b1 commit fc6cbb5

File tree

3 files changed

+37
-6
lines changed

3 files changed

+37
-6
lines changed

rest_framework/fields.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -674,17 +674,16 @@ class BooleanField(Field):
674674
'0', 0, 0.0,
675675
False
676676
}
677-
678-
def __init__(self, **kwargs):
679-
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.'
680-
super(BooleanField, self).__init__(**kwargs)
677+
NULL_VALUES = {'null', 'Null', 'NULL', '', None}
681678

682679
def to_internal_value(self, data):
683680
try:
684681
if data in self.TRUE_VALUES:
685682
return True
686683
elif data in self.FALSE_VALUES:
687684
return False
685+
elif data in self.NULL_VALUES and self.allow_null:
686+
return None
688687
except TypeError: # Input is an unhashable type
689688
pass
690689
self.fail('invalid', input=data)
@@ -694,6 +693,8 @@ def to_representation(self, value):
694693
return True
695694
elif value in self.FALSE_VALUES:
696695
return False
696+
if value in self.NULL_VALUES and self.allow_null:
697+
return None
697698
return bool(value)
698699

699700

@@ -718,7 +719,7 @@ class NullBooleanField(Field):
718719
'0', 0, 0.0,
719720
False
720721
}
721-
NULL_VALUES = {'n', 'N', 'null', 'Null', 'NULL', '', None}
722+
NULL_VALUES = {'null', 'Null', 'NULL', '', None}
722723

723724
def __init__(self, **kwargs):
724725
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'

tests/test_fields.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ def test_disallow_unhashable_collection_types(self):
657657

658658
class TestNullBooleanField(TestBooleanField):
659659
"""
660-
Valid and invalid values for `BooleanField`.
660+
Valid and invalid values for `NullBooleanField`.
661661
"""
662662
valid_inputs = {
663663
'true': True,
@@ -682,6 +682,16 @@ class TestNullBooleanField(TestBooleanField):
682682
field = serializers.NullBooleanField()
683683

684684

685+
class TestNullableBooleanField(TestNullBooleanField):
686+
"""
687+
Valid and invalid values for `BooleanField` when `allow_null=True`.
688+
"""
689+
690+
@property
691+
def field(self):
692+
return serializers.BooleanField(allow_null=True)
693+
694+
685695
# String types...
686696

687697
class TestCharField(FieldValues):

tests/test_model_serializer.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import sys
1313
from collections import OrderedDict
1414

15+
import django
1516
import pytest
1617
from django.core.exceptions import ImproperlyConfigured
1718
from django.core.validators import (
@@ -220,6 +221,25 @@ class Meta:
220221
)
221222
self.assertEqual(unicode_repr(TestSerializer()), expected)
222223

224+
# merge this into test_regular_fields / RegularFieldsModel when
225+
# Django 2.1 is the minimum supported version
226+
@pytest.mark.skipif(django.VERSION < (2, 1), reason='Django version < 2.1')
227+
def test_nullable_boolean_field(self):
228+
class NullableBooleanModel(models.Model):
229+
field = models.BooleanField(null=True, default=False)
230+
231+
class NullableBooleanSerializer(serializers.ModelSerializer):
232+
class Meta:
233+
model = NullableBooleanModel
234+
fields = ['field']
235+
236+
expected = dedent("""
237+
NullableBooleanSerializer():
238+
field = BooleanField(allow_null=True, required=False)
239+
""")
240+
241+
self.assertEqual(unicode_repr(NullableBooleanSerializer()), expected)
242+
223243
def test_method_field(self):
224244
"""
225245
Properties and methods on the model should be allowed as `Meta.fields`

0 commit comments

Comments
 (0)