diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 5730ca571..a77fbb7d2 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -662,53 +662,26 @@ class BooleanField(Field): } def __init__(self, **kwargs): - assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.' + kwargs = self._validate_and_update_kwargs(kwargs) super(BooleanField, self).__init__(**kwargs) - def to_internal_value(self, data): - try: - if data in self.TRUE_VALUES: - return True - elif data in self.FALSE_VALUES: - return False - except TypeError: # Input is an unhashable type - pass - self.fail('invalid', input=data) + def _validate_and_update_kwargs(self, kwargs): + assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.' + return kwargs - def to_representation(self, value): - 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.') - } - initial = None - TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True} - FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False} - NULL_VALUES = {'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): + def _to_internal(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_internal_value(self, data): + if isinstance(data, collections.Hashable): + return self._to_internal(data) 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 elif value in self.FALSE_VALUES: @@ -716,6 +689,32 @@ class NullBooleanField(Field): return bool(value) +class NullBooleanField(BooleanField): + initial = None + default_empty_html = empty + NULL_VALUES = { + 'n', 'N', + 'null', 'Null', 'NULL', + '', + None + } + + def _validate_and_update_kwargs(self, kwargs): + assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.' + kwargs['allow_null'] = True + return kwargs + + def _to_internal(self, data): + if data in self.NULL_VALUES: + return None + return super(NullBooleanField, self)._to_internal(data) + + def to_representation(self, value): + if value in self.NULL_VALUES: + return None + return super(NullBooleanField, self).to_representation(value) + + # String types... class CharField(Field): diff --git a/tests/test_fields.py b/tests/test_fields.py index d6b233227..3f6ad17cc 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -558,6 +558,7 @@ class TestBooleanField(FieldValues): valid_inputs = { 'true': True, 'false': False, + 'on': True, '1': True, '0': False, 1: True, @@ -572,6 +573,7 @@ class TestBooleanField(FieldValues): outputs = { 'true': True, 'false': False, + 'on': True, '1': True, '0': False, 1: True, @@ -602,6 +604,7 @@ class TestNullBooleanField(FieldValues): valid_inputs = { 'true': True, 'false': False, + 'on': True, 'null': None, True: True, False: False, @@ -613,6 +616,7 @@ class TestNullBooleanField(FieldValues): outputs = { 'true': True, 'false': False, + 'on': True, 'null': None, True: True, False: False,