Django: How should I store a money value?

You might want to use the .quantize() method. This will round a decimal value to a certain number of places, the argument you provide specifies the number of places:

>>> from decimal import Decimal
>>> Decimal("12.234").quantize(Decimal("0.00"))
Decimal("12.23")

It can also take an argument to specify what rounding approach you want (different accounting systems might want different rounding). More info in the Python docs.

Below is a custom field that automatically produces the correct value. Note that this is only when it is retrieved from the database, and wont help you when you set it yourself (until you save it to the db and retrieve it again!).

from django.db import models
from decimal import Decimal
class CurrencyField(models.DecimalField):
    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        try:
           return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
        except AttributeError:
           return None

[edit]

added __metaclass__, see Django: Why does this custom model field not behave as expected?


I think you should store it in a decimal format and format it to 00.00 format only then sending it to PayPal, like this:

pricestr = "%01.2f" % price

If you want, you can add a method to your model:

def formattedprice(self):
    return "%01.2f" % self.price

My late to the party version that adds South migrations.

from decimal import Decimal
from django.db import models

try:
    from south.modelsinspector import add_introspection_rules
except ImportError:
    SOUTH = False
else:
    SOUTH = True

class CurrencyField(models.DecimalField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, verbose_name=None, name=None, **kwargs):
        decimal_places = kwargs.pop('decimal_places', 2)
        max_digits = kwargs.pop('max_digits', 10)

        super(CurrencyField, self). __init__(
            verbose_name=verbose_name, name=name, max_digits=max_digits,
            decimal_places=decimal_places, **kwargs)

    def to_python(self, value):
        try:
            return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
        except AttributeError:
            return None

if SOUTH:
    add_introspection_rules([
        (
            [CurrencyField],
            [],
            {
                "decimal_places": ["decimal_places", { "default": "2" }],
                "max_digits": ["max_digits", { "default": "10" }],
            },
        ),
    ], ['^application\.fields\.CurrencyField'])

Money should be stored in money field, which sadly does not exist. Since money is two dimensional value (amount, currency).

There is python-money lib, that has many forks, yet I haven't found working one.


Recommendations:

python-money probably the best fork https://bitbucket.org/acoobe/python-money

django-money recommended by akumria: http://pypi.python.org/pypi/django-money/ (havent tried that one yet).