Possibility to remove trailing zeros on DecimalFields representation (#6514)

* Added normalize parameter to DecimalField to be able to strip trailing zeros. Fixes #6151.

* Updated docs to include normalize option on DecimalField

* Fixed linting error in test_fields

* Removed comment and renamed normalize to normalize_output as suggested in code review

Co-authored-by: Tom Christie <tom@tomchristie.com>
This commit is contained in:
Henrik Wahlgren 2022-11-16 15:31:50 +01:00 committed by GitHub
parent 78cdae6999
commit d5f228dd00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 1 deletions

View File

@ -295,6 +295,7 @@ Corresponds to `django.db.models.fields.DecimalField`.
* `min_value` Validate that the number provided is no less than this value. * `min_value` Validate that the number provided is no less than this value.
* `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file. * `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file.
* `rounding` Sets the rounding mode used when quantising to the configured precision. Valid values are [`decimal` module rounding modes][python-decimal-rounding-modes]. Defaults to `None`. * `rounding` Sets the rounding mode used when quantising to the configured precision. Valid values are [`decimal` module rounding modes][python-decimal-rounding-modes]. Defaults to `None`.
* `normalize_output` Will normalize the decimal value when serialized. This will strip all trailing zeroes and change the value's precision to the minimum required precision to be able to represent the value without loosing data. Defaults to `False`.
#### Example usage #### Example usage

View File

@ -963,10 +963,11 @@ class DecimalField(Field):
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None,
localize=False, rounding=None, **kwargs): localize=False, rounding=None, normalize_output=False, **kwargs):
self.max_digits = max_digits self.max_digits = max_digits
self.decimal_places = decimal_places self.decimal_places = decimal_places
self.localize = localize self.localize = localize
self.normalize_output = normalize_output
if coerce_to_string is not None: if coerce_to_string is not None:
self.coerce_to_string = coerce_to_string self.coerce_to_string = coerce_to_string
if self.localize: if self.localize:
@ -1079,6 +1080,9 @@ class DecimalField(Field):
quantized = self.quantize(value) quantized = self.quantize(value)
if self.normalize_output:
quantized = quantized.normalize()
if not coerce_to_string: if not coerce_to_string:
return quantized return quantized
if self.localize: if self.localize:

View File

@ -1251,6 +1251,27 @@ class TestQuantizedValueForDecimal(TestCase):
assert value == expected_digit_tuple assert value == expected_digit_tuple
class TestNormalizedOutputValueDecimalField(TestCase):
"""
Test that we get the expected behavior of on DecimalField when normalize=True
"""
def test_normalize_output(self):
field = serializers.DecimalField(max_digits=4, decimal_places=3, normalize_output=True)
output = field.to_representation(Decimal('1.000'))
assert output == '1'
def test_non_normalize_output(self):
field = serializers.DecimalField(max_digits=4, decimal_places=3, normalize_output=False)
output = field.to_representation(Decimal('1.000'))
assert output == '1.000'
def test_normalize_coeherce_to_string(self):
field = serializers.DecimalField(max_digits=4, decimal_places=3, normalize_output=True, coerce_to_string=False)
output = field.to_representation(Decimal('1.000'))
assert output == Decimal('1')
class TestNoDecimalPlaces(FieldValues): class TestNoDecimalPlaces(FieldValues):
valid_inputs = { valid_inputs = {
'0.12345': Decimal('0.12345'), '0.12345': Decimal('0.12345'),