Reputation: 11162
I don't understand the behaviour of Django DecimalField.
It's defined as:
A fixed-precision decimal number, represented in Python by a Decimal instance.
However, with the following model:
class Article(models.Model)
unit_price = DecimalField(max_digits=9, decimal_places=2)
I can create an article in at least 3 ways:
article = Article.objects.create(unit_price="2.3")
type(article.unit_price)
>>> str
article = Article.objects.create(unit_price=2.3)
type(article.unit_price)
>>> float
article = Article.objects.create(unit_price=Decimal('2.3'))
type(article.unit_price)
>>> decimal.Decimal
Why Django DecimalField is able to return something else than Decimal type?
What would be the best way to ensure my app never deals with floats for prices?
Thanks.
Upvotes: 6
Views: 6620
Reputation: 7185
Why Django DecimalField is able to return something else than Decimal type?
This is because Django is permissive during model creation and allows you to input any type to these fields without erroring out if what you input can be coerced to the specified type.
After inserting it in the database it gets the right type. You can verify this using refresh_from_db()
:
article = Article.objects.create(unit_price="2.3")
type(article.unit_price)
>>> str
article.refresh_from_db()
type(article.unit_price)
>>> decimal.Decimal
What would be the best way to ensure my app never deals with floats for prices?
The only way to ensure this is to coerce any price input to decimal.Decimal
as soon as you know it's a price amount.
Upvotes: 11
Reputation: 9246
Well, the decimal.Decimal
subtype, is itself a float
in nature, that's why it is accepted in your model. The decimal.Decimal type provides further arithmetic accuracy to the float type.
As explained in the decimal type docs, the operation:
0.1 + 0.1 + 0.1 - 0.3
using decimal.Decimal gives exactly 0
, as in the float type gives a number close to zero:
5.5511151231257827e-017
In the end, decimal.Decimal
numbers can represent exactly.
A decimal number is immutable. It has a sign, coefficient digits, and an exponent. To preserve significance, the coefficient digits do not truncate trailing zeros. Decimals also include special values such as Infinity, -Infinity, and NaN. The standard also differentiates -0 from +0.
In order to have explicit decimals in your application you must be also explicit and pay attention to decimal transformations, every db in/out or serialization operations using the cast to decimal.Decimal
.
To dig further on how the floating point operations have a long story in the background, you can visit this Decimal Arithmetic Specification
Upvotes: 3