## Money database property for Google App Engine

Calculating money is a tricky thing. Your calculations have to be ultra-precise, so no storing things as floats where \$1.33 might actually be stored as 1.3299999999. That is no good for calculations… But equally it is no good storing in cents either: what is 133c /2?

Python has a Decimal datatype, but this requires serialisation to String. Nick Johnson from the Google App Engine team wrote a post about how to write a Decimal property for Google App Engine. Unfortunately this because this serialises to String, then when you do any sorting, then you get String sorting: 100 comes before 11, which comes before 20. Bummer. I played around with storing numbers with a bunch of leading zeroes ie: 0000000010. But that starts to feel a bit hacky.

So I wrote a Money database property that has 6 places of precision, and works as a normal Python numeric type. I haven’t implemented all of those methods, just the ones that I needed. I would be happy to take feedback though.

To use:

```class Transaction(db.model): dateOccurred = db.DateProperty(auto_now_add=True) description = db.StringProperty(required=True) amount = MoneyProperty(required=True)   t = Transaction(description="I got some money", amount=Money(10.34))```
```from google.appengine.ext import db   class Money(object): multiple = 1000000.0 def __init__(self, val, multiply=True): if multiply: self._intVal = int(float(val) * self.multiple) else: self._intVal = int(val)   def format(self, places=2): return "%.*f" % (places, float(self))   def __float__(self): return self._intVal / self.multiple   def __repr__(self): return "%.06f" % (self._intVal / self.multiple)   def __mul__(self, other): if type(other) == Money: return Money((self._intVal * other._intVal) / self.multiple, False) else: return Money(self._intVal * other, False)   __rmul__ = __mul__ def __add__(self, other): if type(other) == Money: return Money(self._intVal + other._intVal, False) else: return Money(self._intVal + (other * self.multiple), False)   __radd__ = __add__   def __cmp__(self, other): if other == None: return 1 elif other == "": return 1 elif type(other) != Money: return self._intVal - other*self.multiple return self._intVal - other._intVal   def __sub__(self, other): return Money(self._intVal - other._intVal, False)   def __rsub__(self, other): return Money(other._intVal - self._intVal, False)   def __div__(self, other): if type(other) == Money: return Money((self._intVal * self.multiple) / other._intVal, False) else: return Money(self._intVal / other, False)   def __rdiv__(self, other): if type(other) == Money: return Money((other._intVal * self.multiple) / self._intVal, False) else: return Money((other * self.multiple * self.multiple) / self._intVal, False)   def __neg__(self): return Money(self._intVal * -1, False)   class MoneyProperty(db.Property): data_type = Money   def get_value_for_datastore(self, model_instance): value = super(MoneyProperty, self).get_value_for_datastore(model_instance) if value==None: return None elif isinstance(value, Money): return value._intVal else: return Money(value)._intVal def make_value_from_datastore(self, value): if value==None: return None else: return Money(value, False)   def empty(self, value): return value == None   def get_value_for_form(self, instance): value = super(MoneyProperty, self).get_value_for_form(instance) if not value: return None if isinstance(value, Money): return float(value) return value   def make_value_from_form(self, value): if not value: return [] if isinstance(value, Money): return Money(value) return value```

This entry was posted on October 14th, 2009.