Skip to main content

Overview

Django Money provides comprehensive support for monetary values in Django applications. Unfold automatically detects and styles MoneyField widgets with the custom UnfoldAdminMoneyWidget, ensuring seamless integration with your admin interface.
Django Money handles currency conversion, formatting, and arithmetic operations while preventing common monetary calculation errors that occur with plain decimal fields.

Installation

1

Install django-money

Install the package using pip:
pip install django-money
2

Add to INSTALLED_APPS

Add djmoney to your settings:
settings.py
INSTALLED_APPS = [
    "unfold",
    "django.contrib.admin",
    # ...
    
    "djmoney",
]
3

Run migrations

Apply django-money migrations:
python manage.py migrate
No additional Unfold contrib app is needed. Unfold automatically styles django-money widgets.

Basic Usage

Model with MoneyField

models.py
from django.db import models
from djmoney.models.fields import MoneyField

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD",
    )
    cost = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD",
        null=True,
        blank=True,
    )
    
    def __str__(self):
        return f"{self.name} - {self.price}"

Admin Configuration

admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
from .models import Product

@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_display = ["name", "price", "cost"]
    search_fields = ["name"]
    list_filter = ["price_currency"]
Unfold automatically applies the UnfoldAdminMoneyWidget to MoneyField fields, providing:
  • Styled currency amount input
  • Currency selection dropdown
  • Consistent design with Unfold theme
  • Support for light and dark modes

Field Options

Default Currency

models.py
class Product(models.Model):
    price = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="EUR",  # Default to Euros
    )

Currency Choices

Limit available currencies:
models.py
from djmoney.models.fields import MoneyField

class Product(models.Model):
    price = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD",
        currency_choices=[("USD", "US Dollar"), ("EUR", "Euro"), ("GBP", "British Pound")],
    )

Nullable Money Fields

models.py
class Product(models.Model):
    discount_price = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD",
        null=True,
        blank=True,
    )

Working with Money Objects

Creating Money Values

from djmoney.money import Money
from myapp.models import Product

# Create product with price
product = Product.objects.create(
    name="Laptop",
    price=Money(999.99, "USD")
)

# Access money field
print(product.price)           # $999.99
print(product.price.amount)    # Decimal('999.99')
print(product.price.currency)  # USD

Money Arithmetic

from djmoney.money import Money

price = Money(100, "USD")
tax = Money(15, "USD")

# Addition
total = price + tax  # Money(115, 'USD')

# Subtraction
discount = Money(20, "USD")
final_price = total - discount  # Money(95, 'USD')

# Multiplication
double_price = price * 2  # Money(200, 'USD')

# Division
half_price = price / 2  # Money(50, 'USD')
Arithmetic operations between Money objects of different currencies will raise an exception. Convert currencies first.

Currency Comparison

price_usd = Money(100, "USD")
price_eur = Money(100, "EUR")

# Same currency comparison works
Money(100, "USD") > Money(50, "USD")  # True

# Different currency comparison raises error
price_usd > price_eur  # Raises error

Querying Money Fields

Filter by Amount

# Products under $100
Product.objects.filter(price__lt=Money(100, "USD"))

# Or filter by amount field directly
Product.objects.filter(price__amount__lt=100)

# Products between $50 and $200
Product.objects.filter(
    price__gte=Money(50, "USD"),
    price__lte=Money(200, "USD")
)

Filter by Currency

# All products priced in USD
Product.objects.filter(price_currency="USD")

# Products not in EUR
Product.objects.exclude(price_currency="EUR")

# Multiple currencies
Product.objects.filter(price_currency__in=["USD", "EUR", "GBP"])

Aggregation

from django.db.models import Sum, Avg, Max, Min

# Total value of all products (same currency)
total = Product.objects.filter(
    price_currency="USD"
).aggregate(total=Sum("price"))  # {'total': Money(5000, 'USD')}

# Average price
average = Product.objects.filter(
    price_currency="USD"
).aggregate(avg_price=Avg("price__amount"))

# Min and max
price_range = Product.objects.aggregate(
    min_price=Min("price__amount"),
    max_price=Max("price__amount")
)

Currency Conversion

Django Money supports currency conversion with exchange rates:

Install Exchange Backend

pip install django-money[exchange]

Configure Exchange Rates

settings.py
INSTALLED_APPS = [
    # ...
    "djmoney.contrib.exchange",
]

# Exchange rate backend
EXCHANGE_BACKEND = "djmoney.contrib.exchange.backends.OpenExchangeRatesBackend"
OPEN_EXCHANGE_RATES_APP_ID = "your-app-id"

Update Exchange Rates

python manage.py update_rates

Convert Currency

from djmoney.money import Money
from djmoney.contrib.exchange.models import convert_money

price_usd = Money(100, "USD")
price_eur = convert_money(price_usd, "EUR")
print(price_eur)  # €85.50 (example rate)

Admin Customization

Custom Money Widget

admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.widgets import UnfoldAdminMoneyWidget
from django import forms
from .models import Product

class ProductAdminForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = "__all__"
        widgets = {
            "price": UnfoldAdminMoneyWidget(),
            "cost": UnfoldAdminMoneyWidget(),
        }

@admin.register(Product)
class ProductAdmin(ModelAdmin):
    form = ProductAdminForm

Display in List

Format money fields in list display:
admin.py
@admin.register(Product)
class ProductAdmin(ModelAdmin):
    list_display = ["name", "formatted_price", "profit_margin"]
    
    def formatted_price(self, obj):
        return obj.price
    formatted_price.short_description = "Price"
    
    def profit_margin(self, obj):
        if obj.cost:
            margin = obj.price - obj.cost
            percentage = (margin.amount / obj.cost.amount) * 100
            return f"{margin} ({percentage:.1f}%)"
        return "-"
    profit_margin.short_description = "Profit Margin"

Advanced Features

Multiple Currency Fields

models.py
class Invoice(models.Model):
    subtotal = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD"
    )
    tax = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD"
    )
    shipping = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD"
    )
    
    @property
    def total(self):
        return self.subtotal + self.tax + self.shipping

Model Methods

models.py
class Product(models.Model):
    price = MoneyField(max_digits=10, decimal_places=2, default_currency="USD")
    cost = MoneyField(max_digits=10, decimal_places=2, default_currency="USD")
    
    def profit(self):
        """Calculate profit margin."""
        return self.price - self.cost
    
    def apply_discount(self, percentage):
        """Apply percentage discount."""
        discount_amount = self.price * (percentage / 100)
        return self.price - discount_amount
    
    def in_currency(self, target_currency):
        """Convert price to different currency."""
        from djmoney.contrib.exchange.models import convert_money
        return convert_money(self.price, target_currency)

Form Validation

forms.py
from django import forms
from djmoney.money import Money
from .models import Product

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ["name", "price", "cost"]
    
    def clean(self):
        cleaned_data = super().clean()
        price = cleaned_data.get("price")
        cost = cleaned_data.get("cost")
        
        if price and cost:
            # Ensure cost is less than price
            if cost >= price:
                raise forms.ValidationError(
                    "Cost must be less than selling price"
                )
            
            # Ensure minimum profit margin
            margin = ((price - cost) / cost) * 100
            if margin < 10:
                raise forms.ValidationError(
                    "Profit margin must be at least 10%"
                )
        
        return cleaned_data

Use Cases

Manage product prices with multiple currencies:
models.py
class Product(models.Model):
    name = models.CharField(max_length=200)
    price = MoneyField(
        max_digits=10,
        decimal_places=2,
        default_currency="USD"
    )
    sale_price = MoneyField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True
    )
    
    @property
    def effective_price(self):
        return self.sale_price or self.price
Track money movements:
models.py
class Transaction(models.Model):
    amount = MoneyField(
        max_digits=12,
        decimal_places=2,
        default_currency="USD"
    )
    fee = MoneyField(
        max_digits=12,
        decimal_places=2,
        default_currency="USD"
    )
    transaction_date = models.DateTimeField(auto_now_add=True)
    
    @property
    def net_amount(self):
        return self.amount - self.fee
Handle subscription pricing:
models.py
class SubscriptionPlan(models.Model):
    name = models.CharField(max_length=100)
    monthly_price = MoneyField(
        max_digits=8,
        decimal_places=2,
        default_currency="USD"
    )
    annual_price = MoneyField(
        max_digits=8,
        decimal_places=2,
        default_currency="USD"
    )
    
    @property
    def annual_savings(self):
        monthly_total = self.monthly_price * 12
        return monthly_total - self.annual_price
Create invoices in different currencies:
models.py
class Invoice(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    total = MoneyField(
        max_digits=12,
        decimal_places=2,
        default_currency="USD"
    )
    currency = models.CharField(max_length=3, default="USD")
    
    def calculate_total(self):
        items_total = sum(
            item.amount for item in self.items.all()
        )
        return items_total

Live Demo

View Money Field Integration

See django-money fields styled with Unfold’s custom widget

Resources

Django Money Docs

Official documentation and API reference

GitHub Repository

Source code and examples
Always use Money objects for monetary calculations instead of plain decimals to avoid precision issues and maintain currency information.

Build docs developers (and LLMs) love