Skip to content

Instantly share code, notes, and snippets.

@jurrian
Created July 21, 2023 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jurrian/bf4faf97d5b34ab9e067700dab539e6b to your computer and use it in GitHub Desktop.
Save jurrian/bf4faf97d5b34ab9e067700dab539e6b to your computer and use it in GitHub Desktop.
CurrencySerializerField for serializing CurrencyField in Django Rest Framework
class CurrencySerializerField(DecimalField):
"""Serialize CurrencyField with 2 decimal places for presentation, keep 4 decimals for internal.
This is implemented by BaseSerializer.
Cannot just set `decimal_places=2` since it will error on input validation when 4 decimals.
For rounding we use "bankers rounding", this is the most common practise but local law can differ.
"""
def __init__(self, *args, max_digits=None, decimal_places=None, **kwargs):
"""Some values are hard coded as they are supposed to be the same for all.
"""
super().__init__(*args, max_digits=10, decimal_places=4, **kwargs)
self.rounding = decimal.ROUND_HALF_EVEN # Bankers rounding
def quantize(self, value: decimal.Decimal):
"""If the value is already quantized to 2 decimals, then leave it be.
This allows to_representation() not to be overruled.
"""
if value.as_tuple().exponent == -2:
return value
return super().quantize(value)
def to_representation(self, value: decimal.Decimal):
"""Displaying results should only show 2 decimals.
The super() calls quantize() where a little trick is applied to preserve 2 decimals.
"""
if not isinstance(value, decimal.Decimal):
value = decimal.Decimal(str(value).strip())
value = value.quantize(
decimal.Decimal('0.00'), # 2 decimals
rounding=self.rounding,
)
return super().to_representation(value)
@jurrian
Copy link
Author

jurrian commented Jul 21, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment