views:

1003

answers:

4

I'm running into a paradigm problem here. I don't know whether I should store money as a Decimal(), or if I should store it as a string and convert it to a decimal myself. My reasoning is this:

PayPal requires 2 decimal places, so if I have a product that is 49 dollars even, PayPal wants to see 49.00 come across the wire. Django's DecimalField() doesn't set a decimal amount. It only stores a maximum decimal places amount. So, if you have 49 in there, and you have the field set to 2 decimal places, it'll still store it as 49. I know that Django is basically type casting when it deserializes back from the database into a Decimal (since Databases don't have decimal fields), so I'm not completely concerned with the speed issues as much as I am with the design issues of this problem. I want to do what's best for extensibility.

Or, better yet, does anyone know how to configure a django DecimalField() to always format with the TWO_PLACES formatting style.

+14  A: 

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
valya
Also: store the currency in the database as well as the amount. Money isn't just a number.
Mr. Shiny and New
True that. Thanks Mr Shiny and New
orokusaki
Thanks valya too.
orokusaki
Mr Shiny, you certainly don't need to store the currency in the database if you know that all the currencies are going to be the same. In fact, most applications don't deal with multiple currencies.
Will Hardy
+4  A: 

I suggest to avoid mixing representation with storage. Store the data as a decimal value with 2 places.

In the UI layer, display it in a form which is suitable for the user (so maybe omit the ".00").

When you send the data to PayPal, format it as the interface requires.

Aaron Digulla
+1  A: 

You store it as a DecimalField and manually add the decimals if you need to, as Valya said, using basic formatting techniques.

You can even add a Model Method to you product or transaction model that will spit out the DecimalField as an appropriately formatted string.

Jasconius
+2  A: 

It's a bit late, but for anyone that stumbles across this, you might want to use the .quantize() method. 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

(NB: this is untested, off the top of my head, but should work!)

[edit]

added __metaclass__, see http://stackoverflow.com/questions/2083591/django-why-does-this-custom-model-field-not-behave-as-expected

Will Hardy
Excellent. Thanks whrde. Is this all you have to do to have a fully functional custom model field? (ie, will this work exactly the same as DecimalField except for the formatting).
orokusaki
Also, what is the convention for storing custom fields? I'm thinking I'll put it in the root project folder.
orokusaki