David Dahan
David Dahan

Reputation: 11162

Why Django DecimalField let me store Float or string?

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

Answers (2)

dukebody
dukebody

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

Evhz
Evhz

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

Related Questions