Repository: MongoEngine/mongoengine

Issue:
DecimalField does not round value at export when force_string is True
In the `to_mongo` method of DecimalField ([link](https://github.com/MongoEngine/mongoengine/blob/9c8ceb6b4ee5676084a0dd534d99146ed2fdae52/mongoengine/fields.py#L328-L333)) there're two behaviors depending on `force_string`. 

The `force_string==False` performs rounding to precision, specified in the field but saves value in float format therefore we don't use it in our project. The `force_string==True` just exports value without any rounding.
Sure enough, it'll be rounded at import due to `to_python` behavior of the field.
In our project that might cause heisenbug if value of the field is used without using mongoengine or skipping the `to_python` step.

What I'm proposing is to modify the [line](https://github.com/MongoEngine/mongoengine/blob/9c8ceb6b4ee5676084a0dd534d99146ed2fdae52/mongoengine/fields.py#L332) in order to use rounding when `force_string==True`. I'm happy to provide pull request.

Please tell me if there's any reason for value not to be rounded when `force_string` is `True`.



Discussion:
I don't see any reason for this. And from the [docs](http://docs.mongoengine.org/en/latest/apireference.html#mongoengine.fields.DecimalField) I would expect rounding to be done whatever the value of `force_string`.

So you're suggesting to change

``` python
if self.force_string:
    return unicode(value)
return float(self.to_python(value))

```

into 

``` python
if self.force_string:
    return unicode(self.to_python(value))
return float(self.to_python(value))
```

right?

Makes sense to me.

This is a quick change. Send a PR and you may raise more interest on this from the maintainers.


Resolving patch:
diff --git a/mongoengine/fields.py b/mongoengine/fields.py
--- a/mongoengine/fields.py
+++ b/mongoengine/fields.py
@@ -341,7 +341,7 @@ def to_mongo(self, value):
         if value is None:
             return value
         if self.force_string:
-            return unicode(value)
+            return unicode(self.to_python(value))
         return float(self.to_python(value))
 
     def validate(self, value):


Test patch:
diff --git a/tests/fields/fields.py b/tests/fields/fields.py
--- a/tests/fields/fields.py
+++ b/tests/fields/fields.py
@@ -483,27 +483,41 @@ class Person(Document):
 
     def test_decimal_storage(self):
         class Person(Document):
-            btc = DecimalField(precision=4)
+            float_value = DecimalField(precision=4)
+            string_value = DecimalField(precision=4, force_string=True)
 
         Person.drop_collection()
-        Person(btc=10).save()
-        Person(btc=10.1).save()
-        Person(btc=10.11).save()
-        Person(btc="10.111").save()
-        Person(btc=Decimal("10.1111")).save()
-        Person(btc=Decimal("10.11111")).save()
+        values_to_store = [10, 10.1, 10.11, "10.111", Decimal("10.1111"), Decimal("10.11111")]
+        for store_at_creation in [True, False]:
+            for value in values_to_store:
+                # to_python is called explicitly if values were sent in the kwargs of __init__
+                if store_at_creation:
+                    Person(float_value=value, string_value=value).save()
+                else:
+                    person = Person.objects.create()
+                    person.float_value = value
+                    person.string_value = value
+                    person.save()
 
         # How its stored
-        expected = [{'btc': 10.0}, {'btc': 10.1}, {'btc': 10.11},
-                    {'btc': 10.111}, {'btc': 10.1111}, {'btc': 10.1111}]
+        expected = [
+            {'float_value': 10.0, 'string_value': '10.0000'},
+            {'float_value': 10.1, 'string_value': '10.1000'},
+            {'float_value': 10.11, 'string_value': '10.1100'},
+            {'float_value': 10.111, 'string_value': '10.1110'},
+            {'float_value': 10.1111, 'string_value': '10.1111'},
+            {'float_value': 10.1111, 'string_value': '10.1111'}]
+        expected.extend(expected)
         actual = list(Person.objects.exclude('id').as_pymongo())
         self.assertEqual(expected, actual)
 
         # How it comes out locally
         expected = [Decimal('10.0000'), Decimal('10.1000'), Decimal('10.1100'),
                     Decimal('10.1110'), Decimal('10.1111'), Decimal('10.1111')]
-        actual = list(Person.objects().scalar('btc'))
-        self.assertEqual(expected, actual)
+        expected.extend(expected)
+        for field_name in ['float_value', 'string_value']:
+            actual = list(Person.objects().scalar(field_name))
+            self.assertEqual(expected, actual)
 
     def test_boolean_validation(self):
         """Ensure that invalid values cannot be assigned to boolean fields.