I need to store a U.S. $
dollar amount in a field of a Django model. What is the best model field type to use? I need to be able to have the user enter this value (with error checking, only want a number accurate to cents), format it for output to users in different places, and use it to calculate other numbers.

- 19,475
- 3
- 20
- 33

- 45,039
- 49
- 151
- 227
6 Answers
A decimal field is the right choice for the currency value.
It will look something like:
credit = models.DecimalField(max_digits=6, decimal_places=2)

- 2,387
- 2
- 8
- 12

- 35,488
- 12
- 65
- 73
-
163Unless you want to represent the national debt, in which case max_digits has to be > 20 – Bron Davies Aug 17 '16 at 14:44
-
5decimal_places=2 is not necessarily correct if you need this to support other currencies. Some currencies have three decimal places, and Bitcoin has a whooping 8. – Lie Ryan Jan 13 '18 at 13:29
-
3I think this example is correct almost to all currencies. To represent currencies as Bitcoin, I think is much better to use an integer field to save the amount in satoshis, and then show it to the final user in the representation that you want (BTC, mBTC, etc) – jion Jan 21 '18 at 06:01
-
@LieRyan that's true. We have to take note of modern types of "currency" to prevent any future db modifications. No offence for the use of quotes. – enchance Mar 04 '20 at 07:02
-
@BronDavies SAVAGE – NoName Jun 15 '20 at 21:21
-
3This answer https://stackoverflow.com/questions/224462/storing-money-in-a-decimal-column-what-precision-and-scale/ suggests using `max_digits=19, decimal_place=4` to be safe. – Boris Verkhovskiy Jul 23 '20 at 20:14
The other answers are 100% right but aren't very practical as you'll still have to manually manage output, formatting etc.
I would suggest using django-money:
from djmoney.models.fields import MoneyField
from django.db import models
def SomeModel(models.Model):
some_currency = MoneyField(
decimal_places=2,
default=0,
default_currency='USD',
max_digits=11,
)
Works automatically from templates:
{{ somemodel.some_currency }}
Output:
$123.00
It has a powerful backend via python-money and it's essentially a drop-in replacement for standard decimal fields.

- 4,181
- 1
- 29
- 24
-
*(with error checking, only want a number accurate to cents), format it for output to users in different places, and use it to calculate other numbers.* For such cases, use `DecimalField` is more practical. And I believe it is applied for most people. Using `django-money` as they highlighted, include **Currency** handling. So this is more for project involving transaction such as ecommerce sites, payment gateway, digital currency, banking, etc.... – Yeo Jul 19 '16 at 22:34
-
I'd argue that since they're working with USD -- a currency -- it makes more sense to use a currency-handling library like django-money (python-money) as it makes everything work as you'd expect and affords future feature adjustments. The original question says they need formatting and calculation (as you quoted) and for that you're better off using a currency library instead of a simple decimal field. – Michael Thompson Jul 20 '16 at 02:30
-
1
-
2@saran3h you can work with money instances and perform calculations like any other number (decimal) instance. You can add/subtract, multiply by integers, etc. – Michael Thompson Jul 12 '18 at 16:42
field = models.DecimalField(max_digits=8, decimal_places=2)
Note that max_digits should be >= decimal_places. This example setting would allow a value up to: 999,999.99
Docs: https://docs.djangoproject.com/en/1.10/ref/models/fields/#decimalfield

- 1,043
- 12
- 13
Define a decimal and return a $ sign in front of the value.
price = models.DecimalField(max_digits=8, decimal_places=2)
@property
def price_display(self):
return "$%s" % self.price
-
8You do realize that you just created an infinite recursion loop in your property, right? – El Ninja Trepador Feb 14 '18 at 12:08
-
field = models.DecimalField(max_digits=8, decimal_places=2)
Should create a field for PostgreSQL like:
"field" numeric(8, 2) NOT NULL
Which is the best way for PostGreSQL stored US dollar amount.
If you need a PostgreSQL field type "double precision", then you need do in django model:
field = models.FloatField()

- 1,283
- 1
- 14
- 20
-
3Beware of representing money as a floating point number, as people tend to get unhappy about rounding errors w.r.t. their money. See: https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency for more details. – Adam Parkin Oct 03 '18 at 22:00
You can use django-money for money
field:
First, install django-money[exchange]
as shown below:
pip install django-money[exchange]
*You can install django-money
without [exchange]
as shown below but it doesn't have the functions to convert currencies so I recommend to install django-money[exchange]
as shown above:
pip install django-money
Next, add 'djmoney.contrib.exchange'
to INSTALLED_APPS
in core/settings.py
:
# "core/settings.py"
INSTALLED_APPS = [
...,
'djmoney.contrib.exchange',
]
Then, run the command below:
python manage.py migrate
Then, define a field with MoneyField()
, MinMoneyValidator()
, MaxMoneyValidator()
and Decimal in MyModel
model in `my_app/models.py' as shown below:
# "my_app/models.py"
from djmoney.models.fields import MoneyField
from decimal import Decimal
from djmoney.models.validators import MaxMoneyValidator, MinMoneyValidator
class MyModel(models.Model):
money = MoneyField(
max_digits=5, decimal_places=2, default=0, default_currency='USD',
validators=[
MinMoneyValidator(Decimal(0.00)), MaxMoneyValidator(Decimal(999.99)),
]
)
Then, run the command below:
python manage.py makemigrations && python manage.py migrate
Then, you can add a value on Django Admin as shown below:
Then, you can get the value with $
and the value without $
using .amount
in my_app/views.py
as shown below:
# "my_app/views.py"
from django.http import HttpResponse
from app.models import MyModel
def test(request):
print(MyModel.objects.all()[0].money) # Here
print(MyModel.objects.all()[0].money.amount) # Here
return HttpResponse("Test")
Then, these below are displayed on console:
$12.54
12.54
Next, you can convert 12.54 USD
to ... EUR
.
First, go to Open Exchange Rates, then sign up to get the exchange rates of currencies:
Then, copy App ID from your dashboard:
Then, set OPEN_EXCHANGE_RATES_APP_ID
with App ID in core/settings.py
:
# "core/settings.py"
# Here
OPEN_EXCHANGE_RATES_APP_ID = '368183b0b2644e999ef2a61bd38d0ca3'
Then, run the command below:
python manage.py update_rates
Then, you can convert 12.54 USD
to ... EUR
with convert_money()
and Money()
as shown below. *You have to use the value without $
using .amount
:
# "my_app/views.py"
from django.http import HttpResponse
from app.models import MyModel
from djmoney.contrib.exchange.models import convert_money
from djmoney.money import Money
def test(request):
money = MyModel.objects.all()[0].money.amount
print(convert_money(Money(money, 'USD'), 'EUR')) # Here
return HttpResponse("Test")
Then, 12.54 USD
is converted to 11.70 EUR
as shown below:
€11.70
You can see django-money doc which has more details.
And, you can update the exchange rates of currencies every 60 minutes with celery beat code in core/tasks.py
and core/settings.py
as shown below. *Open Exchange Rates can accept 1000 requests per month in Free Plan:
# "core/tasks.py"
from celery import shared_task
from djmoney import settings
from django.utils.module_loading import import_string
@shared_task
def update_rates(app_id):
backend = import_string(settings.EXCHANGE_BACKEND)(
access_key=app_id
)
backend.update_rates()
print("Successfully updated")
*You should put OPEN_EXCHANGE_RATES_APP_ID
and import app
, crontab
and update_rates
just before the celery beat code in settings.py
as shown below:
# "core/settings.py"
OPEN_EXCHANGE_RATES_APP_ID = '368183b0b2644e999ef2a61bd38d0ca3'
from .celery import app
from celery.schedules import crontab
from .tasks import update_rates
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
sender.add_periodic_task(
crontab(minute='*/60'),
update_rates.s(OPEN_EXCHANGE_RATES_APP_ID),
name='update_rates'
)

- 22,221
- 10
- 124
- 129