65π
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?
18π
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
- [Django]-How to check the TEMPLATE_DEBUG flag in a django template?
- [Django]-Django filter the model on ManyToMany count?
- [Django]-Python/Django: log to console under runserver, log to file under Apache
13π
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'])
- [Django]-Django: Reference to an outer query may only be used in a subquery
- [Django]-How to debug in Django, the good way?
- [Django]-How to obtain a QuerySet of all rows, with specific fields for each one of them?
12π
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).
- [Django]-How to run Django's test database only in memory?
- [Django]-How to check the TEMPLATE_DEBUG flag in a django template?
- [Django]-Django: Reverse for 'detail' with arguments '('',)' and keyword arguments '{}' not found
10π
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.
- [Django]-Django-allauth: Linking multiple social accounts to a single user
- [Django]-Can I call a view from within another view?
- [Django]-Django self-referential foreign key
4π
In my experience and also from others, money is best stored as combination of currency and the amount in cents.
Itβs very easy to handle and calculate with it.
- [Django]-Django β how to detect test environment (check / determine if tests are being run)
- [Django]-Storing an Integer Array in a Django Database
- [Django]-Fighting client-side caching in Django
4π
Building on @Will_Hardyβs answer, here it is so you donβt have to specify max_digits and decimal_places every time:
from django.db import models
from decimal import Decimal
class CurrencyField(models.DecimalField):
__metaclass__ = models.SubfieldBase
def __init__(self, verbose_name=None, name=None, **kwargs):
super(CurrencyField, self). __init__(
verbose_name=verbose_name, name=name, max_digits=10,
decimal_places=2, **kwargs)
def to_python(self, value):
try:
return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
except AttributeError:
return None
- [Django]-Django β {% csrf_token %} was used in a template, but the context did not provide the value
- [Django]-Django Rest Framework with ChoiceField
- [Django]-Django ModelForm to have a hidden input
1π
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.
- [Django]-Django Generic Views using decorator login_required
- [Django]-Django, query filtering from model method
- [Django]-How to paginate Django with other get variables?
0π
Iβm first-hands-on on Django and storing money values, too.
There is another possibility to store money values: just convert a money value to its minor representation and store that integer value in the database. If the rounding of the db backend can be adjusted to monetary arithmetic needs, this should work.
When retrieving a value, just read it back and represent it as a string as needed, which usually involves placing a decimal separator at the right position after converting the integer to a string.
It looks that some middleware exactly expects such behaviour Convert decimal to integer without losing monetary value.
- [Django]-How to find pg_config path
- [Django]-Get user information in django templates
- [Django]-How can I render a tree structure (recursive) using a django template?
0π
A better idea is to store money in minor currency units (smallest units possible). For euros and dollars the minor currency units are cents. Thereβs a Python (and Django) package called valuta based on the concept.
Using currency classes directly
import valuta
valuta.EUR.convert_to_currency_units(1_000) # 1_000 is the price in cents
# 10.0
Working with string representations of the (ISO-4217) currency codes
from valuta.shortcuts import convert_to_currency_units
convert_to_currency_units("EUR", 1_000) # 1_000 is the price in cents
# 10.0
Django integration
Model definition
from django.db import models
from valuta.contrib.django_integration.models import CurrencyField
class Product(models.Model):
price = models.IntegerField() # Amount in minor currency units
price_with_tax = models.IntegerField() # Amount in minor currency units
currency = CurrencyField(amount_fields=["price", "price_with_tax"])
Sample data
import valuta
from product.models import Product
product = Product.objects.create(
price=1_000, # Price in cents
price_with_tax=1_200, # Price in cents
currency=valuta.EUR.uid,
)
Use magic methods to convert from minor currency (cents) units to major currency units (euros)
product.price_in_currency_units() # Price in euros
# 10.0
product.price_with_tax_in_currency_units() # Price in euros
# 12.0
If you need to display it in templates:
product.price_display_in_currency_units() # Price in euros
# '10.00'
product.price_with_tax_display_in_currency_units() # Price in euros
# '12.00'
Now try another currency (JPY).
product_jpy = Product.objects.create(
price=1_000, # Price in cents
price_with_tax=1_200, # Price in cents
currency=valuta.JPY.uid,
)
product.price_in_currency_units() # Price in JPY
# 10.0
product.price_with_tax_in_currency_units() # Price in JPY
# 12.0
product.price_display_in_currency_units() # Price in JPY
# '10'
product.price_with_tax_display_in_currency_units() # Price in JPY
# '12'
- [Django]-Error loading MySQLdb Module 'Did you install mysqlclient or MySQL-python?'
- [Django]-Django: Display Choice Value
- [Django]-Get last record in a queryset