From 1fbe16a8d26ff5be64797cafb7004898f72ca52b Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Tue, 6 Dec 2022 06:04:50 -0300 Subject: [PATCH] Fix BooleanField's allow_null behavior (#8614) * Fix BooleanField's allow_null behavior * Update rest_framework.fields - Use .get with default value for 'allow_null' kwarg in BooleanField's init --- rest_framework/fields.py | 6 ++++++ tests/test_fields.py | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index cb1b28167..799dd94f9 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -690,6 +690,12 @@ class BooleanField(Field): } NULL_VALUES = {'null', 'Null', 'NULL', '', None} + def __init__(self, **kwargs): + if kwargs.get('allow_null', False): + self.default_empty_html = None + self.initial = None + super().__init__(**kwargs) + def to_internal_value(self, data): with contextlib.suppress(TypeError): if data in self.TRUE_VALUES: diff --git a/tests/test_fields.py b/tests/test_fields.py index 51a661ddc..5fd075d40 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -371,7 +371,7 @@ class TestBooleanHTMLInput: def test_empty_html_checkbox(self): """ HTML checkboxes do not send any value, but should be treated - as `False` by BooleanField. + as `False` by BooleanField if allow_null=False. """ class TestSerializer(serializers.Serializer): archived = serializers.BooleanField() @@ -383,7 +383,8 @@ class TestBooleanHTMLInput: def test_empty_html_checkbox_not_required(self): """ HTML checkboxes do not send any value, but should be treated - as `False` by BooleanField, even if the field is required=False. + as `False` by BooleanField when the field is required=False + and allow_null=False. """ class TestSerializer(serializers.Serializer): archived = serializers.BooleanField(required=False) @@ -392,6 +393,34 @@ class TestBooleanHTMLInput: assert serializer.is_valid() assert serializer.validated_data == {'archived': False} + def test_empty_html_checkbox_allow_null(self): + """ + HTML checkboxes do not send any value and should be treated + as `None` by BooleanField if allow_null is True. + """ + class TestSerializer(serializers.Serializer): + archived = serializers.BooleanField(allow_null=True) + + serializer = TestSerializer(data=QueryDict('')) + assert serializer.is_valid() + assert serializer.validated_data == {'archived': None} + + def test_empty_html_checkbox_allow_null_with_default(self): + """ + BooleanField should respect default if set and still allow + setting null values. + """ + class TestSerializer(serializers.Serializer): + archived = serializers.BooleanField(allow_null=True, default=True) + + serializer = TestSerializer(data=QueryDict('')) + assert serializer.is_valid() + assert serializer.validated_data == {'archived': True} + + serializer = TestSerializer(data=QueryDict('archived=')) + assert serializer.is_valid() + assert serializer.validated_data == {'archived': None} + class TestHTMLInput: def test_empty_html_charfield_with_default(self):