Repository: encode/django-rest-framework

Issue:
Sending "" to a DecimalField with a min or a max value validator raises a TypeError
Example code:
```python
from rest_framework import serializers
from decimal import Decimal
class Foo(serializers.Serializer):
    bar = serializers.DecimalField(max_digits=12, decimal_places=2, allow_null=True, min_value=Decimal('0.01'))
    
Foo(data={'bar': ''}).is_valid()
```

Result:
`TypeError: '<' not supported between instances of 'NoneType' and 'decimal.Decimal'`

This is due to the value being changed to None here after https://github.com/encode/django-rest-framework/pull/7718:
https://github.com/encode/django-rest-framework/blob/d2977cff989f9b14f402ecf1e9235ee3d110977b/rest_framework/fields.py#L1063-L1067


This doesn't happen with 
```python
Foo(data={'bar': None}).is_valid()
```

I'm guessing in the latter case the min/max validation is skipped. I'm not sure if the same should happen here or if an empty string should not be accepted as a valid value.


Discussion:
Good catch!

A good starting point here would be for someone to implement a pull request that adds a failing test for this particular case. Then we can get the test case resolved after that. Having a look at the tests added in #7718 would be helpful for anyone taking a go at this.

Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -1046,6 +1046,11 @@ def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=
                 'Invalid rounding option %s. Valid values for rounding are: %s' % (rounding, valid_roundings))
         self.rounding = rounding
 
+    def validate_empty_values(self, data):
+        if smart_str(data).strip() == '' and self.allow_null:
+            return (True, None)
+        return super().validate_empty_values(data)
+
     def to_internal_value(self, data):
         """
         Validate that the input is a decimal number and return a Decimal
@@ -1063,9 +1068,6 @@ def to_internal_value(self, data):
         try:
             value = decimal.Decimal(data)
         except decimal.DecimalException:
-            if data == '' and self.allow_null:
-                return None
-
             self.fail('invalid')
 
         if value.is_nan():


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -1163,6 +1163,30 @@ class TestMinMaxDecimalField(FieldValues):
     )
 
 
+class TestAllowEmptyStrDecimalFieldWithValidators(FieldValues):
+    """
+    Check that empty string ('', ' ') is acceptable value for the DecimalField
+    if allow_null=True and there are max/min validators
+    """
+    valid_inputs = {
+        None: None,
+        '': None,
+        ' ': None,
+        '  ': None,
+        5: Decimal('5'),
+        '0': Decimal('0'),
+        '10': Decimal('10'),
+    }
+    invalid_inputs = {
+        -1: ['Ensure this value is greater than or equal to 0.'],
+        11: ['Ensure this value is less than or equal to 10.'],
+    }
+    outputs = {
+        None: '',
+    }
+    field = serializers.DecimalField(max_digits=3, decimal_places=1, allow_null=True, min_value=0, max_value=10)
+
+
 class TestNoMaxDigitsDecimalField(FieldValues):
     field = serializers.DecimalField(
         max_value=100, min_value=0,