diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f4fd26539..7b934ca39 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -678,22 +678,27 @@ class BooleanField(Field): default_empty_html = False initial = False TRUE_VALUES = { - 't', 'T', - 'y', 'Y', 'yes', 'Yes', 'YES', - 'true', 'True', 'TRUE', - 'on', 'On', 'ON', - '1', 1, - True + 't', + 'y', + 'yes', + 'true', + 'on', + '1', + 1, + True, } FALSE_VALUES = { - 'f', 'F', - 'n', 'N', 'no', 'No', 'NO', - 'false', 'False', 'FALSE', - 'off', 'Off', 'OFF', - '0', 0, 0.0, - False + 'f', + 'n', + 'no', + 'false', + 'off', + '0', + 0, + 0.0, + False, } - NULL_VALUES = {'null', 'Null', 'NULL', '', None} + NULL_VALUES = {'null', '', None} def __init__(self, **kwargs): if kwargs.get('allow_null', False): @@ -701,22 +706,30 @@ class BooleanField(Field): self.initial = None super().__init__(**kwargs) + @staticmethod + def _lower_if_str(value): + if isinstance(value, str): + return value.lower() + return value + def to_internal_value(self, data): - with contextlib.suppress(TypeError): - if data in self.TRUE_VALUES: + try: + if self._lower_if_str(data) in self.TRUE_VALUES: return True - elif data in self.FALSE_VALUES: + elif self._lower_if_str(data) in self.FALSE_VALUES: return False - elif data in self.NULL_VALUES and self.allow_null: + elif self._lower_if_str(data) in self.NULL_VALUES and self.allow_null: return None - self.fail('invalid', input=data) + except TypeError: # Input is an unhashable type + pass + self.fail("invalid", input=data) def to_representation(self, value): - if value in self.TRUE_VALUES: + if self._lower_if_str(value) in self.TRUE_VALUES: return True - elif value in self.FALSE_VALUES: + elif self._lower_if_str(value) in self.FALSE_VALUES: return False - if value in self.NULL_VALUES and self.allow_null: + if self._lower_if_str(value) in self.NULL_VALUES and self.allow_null: return None return bool(value) diff --git a/tests/test_fields.py b/tests/test_fields.py index bf25b71b8..4ff741e56 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -690,8 +690,24 @@ class TestBooleanField(FieldValues): Valid and invalid values for `BooleanField`. """ valid_inputs = { + 'True': True, + 'TRUE': True, + 'tRuE': True, + 't': True, + 'T': True, 'true': True, + 'on': True, + 'ON': True, + 'oN': True, + 'False': False, + 'FALSE': False, + 'fALse': False, + 'f': False, + 'F': False, 'false': False, + 'off': False, + 'OFF': False, + 'oFf': False, '1': True, '0': False, 1: True, @@ -704,8 +720,24 @@ class TestBooleanField(FieldValues): None: ['This field may not be null.'] } outputs = { + 'True': True, + 'TRUE': True, + 'tRuE': True, + 't': True, + 'T': True, 'true': True, + 'on': True, + 'ON': True, + 'oN': True, + 'False': False, + 'FALSE': False, + 'fALse': False, + 'f': False, + 'F': False, 'false': False, + 'off': False, + 'OFF': False, + 'oFf': False, '1': True, '0': False, 1: True, @@ -726,7 +758,7 @@ class TestBooleanField(FieldValues): with pytest.raises(serializers.ValidationError) as exc_info: field.run_validation(input_value) expected = ['Must be a valid boolean.'] - assert exc_info.value.detail == expected + assert exc_info.value.detail == expected class TestNullableBooleanField(TestBooleanField): @@ -2638,4 +2670,4 @@ class TestValidationErrorCode: code=expected_code ), ] - } + } \ No newline at end of file