MongoDB added a new native type for handling floating point numbers called NumberDecimal in 3.4.
See the following documentation for details:
https://docs.mongodb.com/manual/tutorial/model-monetary-data/#using-the-decimal-bson-type
MongoEngine lacks a corresponding field that allows you to store decimal values as a NumberDecimal.
{
"total" : NumberDecimal("31.73")
}
The closest two fields are DecimalField and FloatField which store values as a decimal, and is unsuitable for monetary arithmetic (subject to rounding errors) .
{
"total" : 31.73
}
There are some comments on other issues that relate to this one:
Anyone have ideas on the best way to add support for this?
I think adding a new NumberDecimalField field may be appropriate.
We could call the new field CurrencyField to emphasize this field is suitable for monetary arithmetic.
Also, since NumberDecimal is only supported in MongoDB 3.4 and greater, how do we handle it in the library? Do we just put a warning in the documentation, or raise an error if they try using the field with a version of MongoDB < 3.4?
One approach is to use IntField and the Scale Factor approach.
Ideally, I'd like a work-around to store values using NumberDecimal as the Scale Factor approach has overhead involved.
According to this comment, if you cast the values to bson.Decimal128 before inserting into MongoDB, then the values are stored as NumberDecimal.
I tried overloading the save method on my Model, but the value still appears to be stored as a decimal float value in MongoDB.
def save(self, *args, **kwargs):
self['total'] = Decimal128(self['total'])
super().save(*args, **kwargs)
{
"total" : 31.73
}
Kind of hacky, but this seems to work in a pinch.
from bson.decimal128 import Decimal128
class Decimal128Field(StringField):
"""128-bit decimal-based floating-point field capable of emulating decimal rounding with exact precision.
Stores the value as a `NumberDecimal` intended for monetary data, such as financial, tax, and scientific computations.
"""
def to_mongo(self, value):
if isinstance(value, Decimal128):
return value
return Decimal128(value)
def to_python(self, value):
return self.to_mongo(value)
def validate(self, value):
if not isinstance(value, Decimal128):
try:
value = Decimal128(value)
except (TypeError, ValueError) as exc:
self.error('Could not convert value to Decimal128: %s' % exc)
def prepare_query_value(self, op, value):
return super(Decimal128Field, self).prepare_query_value(op, self.to_mongo(value))
class SomeDocument(Document):
total = Decimal128Field(required=True)
Just checking in to see if you guys support NumberDecimal yet
@toniree I no longer actively use MongoEngine, and it seems like this issue never received any attention.
My work-around was documented at the bottom of the issue (see the Decimal128Field class).
I hope that helps :)
Most helpful comment
@toniree I no longer actively use MongoEngine, and it seems like this issue never received any attention.
My work-around was documented at the bottom of the issue (see the
Decimal128Fieldclass).I hope that helps :)