Django Money
Django Money is a little Django app that integrates the `py-moneyed` library to add robust support for money and currency fields in Django models and forms. It ensures accurate handling of monetary values, currency conversions, and formatting. The current version, 3.6.0, supports modern Django and Python versions and is actively maintained with regular releases.
Warnings
- breaking When upgrading to `django-money` 3.x from older versions (e.g., 2.x or 0.x), several features have been removed or renamed. Specifically, `Money.decimal_places_display` and `CURRENCY_DECIMAL_PLACES_DISPLAY` settings were removed in version 3.0. Additionally, support for older Python (2.6, 3.2) and Django (1.4-1.7, 1.9, 2.2, 3.0, 3.1, 4.0) versions has been dropped in various 3.x releases.
- deprecated The import path `from moneyed import Money` has been deprecated in favor of `from djmoney.money import Money`. Similarly, `djmoney.models.fields.MoneyPatched` was deprecated.
- gotcha For money calculations, always use Python's `Decimal` type via `djmoney.money.Money` objects instead of floating-point numbers (`float`). Floats can introduce precision errors crucial for financial data.
- gotcha To ensure money fields are correctly displayed and integrated with Django's admin and localization features, you must add `'djmoney'` to your `INSTALLED_APPS` in `settings.py` and set `USE_L10N = True` for localization-aware formatting.
- gotcha When working with custom model managers on models containing `MoneyField`, you need to explicitly wrap your manager using `djmoney.models.managers.money_manager` to enable proper filtering and querying based on money values.
- gotcha The `MoneyField` internally stores two values (`amount` and `currency`). While ORM queries generally work with the `Money` object, direct database interactions or complex custom queries might require referencing the underlying `_amount` and `_currency` fields (e.g., `myfield_amount`, `myfield_currency`).
Install
-
pip install django-money -
pip install "django-money[exchange]"
Imports
- MoneyField
from djmoney.models.fields import MoneyField
- Money
from djmoney.money import Money
- money_manager
from djmoney.models.managers import money_manager
- money_is_not_zero
from djmoney.models.validators import money_is_not_zero
- money_localize (template tag)
{% load djmoney %}
Quickstart
import os
import django
from django.conf import settings
from django.db import models
settings.configure(
INSTALLED_APPS=[
'djmoney',
'tests',
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
DEFAULT_CURRENCY='USD',
USE_L10N=True, # Enable localization for formatting
)
django.setup()
from djmoney.models.fields import MoneyField
from djmoney.money import Money
class BankAccount(models.Model):
balance = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
# It's also possible to have a nullable MoneyField:
# money = MoneyField(max_digits=10, decimal_places=2, null=True, default_currency=None)
def __str__(self):
return f"{self.balance} - {self.pk}"
# Run migrations (simplified for quickstart)
with open(os.devnull, 'w') as f:
import sys
_stdout = sys.stdout
sys.stdout = f
try:
from django.core.management import call_command
call_command('makemigrations', 'tests', verbosity=0, interactive=False)
call_command('migrate', verbosity=0, interactive=False)
finally:
sys.stdout = _stdout
# Create an account
account = BankAccount.objects.create(balance=Money(100, 'USD'))
print(f"Created account: {account}")
# Querying
expensive_accounts = BankAccount.objects.filter(balance__gt=Money(50, 'USD'))
print(f"Accounts with balance > $50: {list(expensive_accounts)}")
# Accessing amount and currency separately
print(f"Account balance amount: {account.balance.amount}")
print(f"Account balance currency: {account.balance.currency}")
# Example of template tag usage (conceptual, as this is a console quickstart)
# In a Django template:
# {% load djmoney %}
# <p>Current balance: {{ account.balance|money_localize }}</p>