Improve DecimalField
for easier subclassing. by delinhabit · Pull Request #2695 · encode/django-rest-framework (original) (raw)
Hello,
I did some cleanup in the DecimalField class to make the methods shorter and more focused on their responsibility, but the main goal is to allow developer do to something like this:
class PermissiveDecimalField(serializers.DecimalField):
"""
Consider the configured max_digits
and decimal_places
for both
serialization and deserialization, but do not reject input values with
bigger precisions.
"""
def validate_precision(self, value):
return self.quantize(value)
Without this refactor, covering such a use-case is quite ugly. You have to copy paste the to_internal_value
to benefit for the sanity checks and then override the precision validation part:
class PermissiveDecimalField(serializers.DecimalField):
"""
Consider the configured max_digits
and decimal_places
for both
serialization and deserialization, but do not reject input values with
bigger precisions.
"""
def to_internal_value(self, data):
data = smart_text(data).strip()
if len(data) > self.MAX_STRING_LENGTH:
self.fail('max_string_length')
try:
value = decimal.Decimal(data)
except decimal.DecimalException:
self.fail('invalid')
# Check for NaN. It is the only value that isn't equal to itself,
# so we can use this to identify NaN values.
if value != value:
self.fail('invalid')
# Check for infinity and negative infinity.
if value in (decimal.Decimal('Inf'), decimal.Decimal('-Inf')):
self.fail('invalid')
return self.validate_precision(value)
def validate_precision(self, value):
context = decimal.getcontext().copy()
context.prec = self.max_digits
return value.quantize(
decimal.Decimal('.1') ** self.decimal_places,
context=context)
And if you think about it, it's not an obscure use-case at all. In my case, we are storing longitude and latitude values of up to 6 decimal places (~100 millimeters precision, which is more than enough for us) but the values can come from different geolocation sources which can have bigger precision and without this change we would have to sanitize the inputs on the client side. On each different client that you have. This way, I don't care about the infinitesimal precisions, I just grab the value, quantize it to the desired precision and store it in the database.
I like this sort of refactoring because if makes the code cleaner and the life of the developers easier. (I'm happy to do more of these if the community agrees that it's valuable).
Any thoughts?
Thanks!