From 05512160abb4c2110afff9e82f8f523be68476cf Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Tue, 9 Mar 2021 05:49:03 -0500 Subject: [PATCH] Respect allow_null=True on DecimalFields (#7718) * Handle None in to_representation() * Return None as '' in to_representation() when coerce_to_string=True * Handle '' as None in to_internal_value(), for symmetry with to_representation(), and because the empty concept doesn't make sense for Decimal. --- rest_framework/fields.py | 9 +++++++++ tests/test_fields.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index b6c9ee5c5..d91299484 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1063,6 +1063,9 @@ class DecimalField(Field): try: value = decimal.Decimal(data) except decimal.DecimalException: + if data == '' and self.allow_null: + return None + self.fail('invalid') if value.is_nan(): @@ -1112,6 +1115,12 @@ class DecimalField(Field): def to_representation(self, value): coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING) + if value is None: + if coerce_to_string: + return '' + else: + return None + if not isinstance(value, decimal.Decimal): value = decimal.Decimal(str(value).strip()) diff --git a/tests/test_fields.py b/tests/test_fields.py index fdd570d8a..5842553f0 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1090,6 +1090,9 @@ class TestDecimalField(FieldValues): '2E+1': Decimal('20'), } invalid_inputs = ( + (None, ["This field may not be null."]), + ('', ["A valid number is required."]), + (' ', ["A valid number is required."]), ('abc', ["A valid number is required."]), (Decimal('Nan'), ["A valid number is required."]), (Decimal('Snan'), ["A valid number is required."]), @@ -1115,6 +1118,32 @@ class TestDecimalField(FieldValues): field = serializers.DecimalField(max_digits=3, decimal_places=1) +class TestAllowNullDecimalField(FieldValues): + valid_inputs = { + None: None, + '': None, + ' ': None, + } + invalid_inputs = {} + outputs = { + None: '', + } + field = serializers.DecimalField(max_digits=3, decimal_places=1, allow_null=True) + + +class TestAllowNullNoStringCoercionDecimalField(FieldValues): + valid_inputs = { + None: None, + '': None, + ' ': None, + } + invalid_inputs = {} + outputs = { + None: None, + } + field = serializers.DecimalField(max_digits=3, decimal_places=1, allow_null=True, coerce_to_string=False) + + class TestMinMaxDecimalField(FieldValues): """ Valid and invalid values for `DecimalField` with min and max limits.