Skip to Content
FeaturesModulesCurrencyCurrency System Overview

Currency System

Django-CFG’s Currency System provides database-backed exchange rates with automatic updates, a composite model field for storing monetary values, and seamless Django Admin integration.

Architecture

The currency system consists of three layers:

Layer 1: Your Code

What you write in your Django project:

ComponentPurpose
MoneyFieldModel field that stores amount + currency
PydanticAdminAdmin base class (includes currency support)
MoneyFieldDisplayDisplay field for admin lists

Layer 2: Django-CFG Modules

Internal components that handle the heavy lifting:

ComponentLocationPurpose
MoneyFielddjango_cfg.modules.django_currencyCreates composite fields
MoneyFieldAdminMixindjango_cfg.modules.django_currencyAdmin form handling
MoneyFieldWidgetdjango_cfg.modules.django_admin.widgetsLive conversion widget

Layer 3: Currency App

Built-in app that manages exchange rates:

ComponentLocationPurpose
CurrencyRatedjango_cfg.apps.tools.currencyDatabase model (~50 rows)
CurrencyConverterdjango_cfg.apps.tools.currency.servicesRate lookup
update_all_ratesdjango_cfg.apps.tools.currency.tasksRQ scheduled task

Philosophy

”Rates in Database, Not Per Record”

Anti-pattern: Storing price_usd, price_rate, price_rate_at on every record.

With 1M records × 3 fields = massive storage and expensive hourly updates.

Our approach: Single CurrencyRate table (~50 rows) updated hourly.

MoneyField.price_target is a property that reads from this table on access.

”Configure Once, Works Everywhere”

Enable currency in your config, and everything auto-registers:

from django_cfg import DjangoConfig, CurrencyConfig class MyConfig(DjangoConfig): # Minimal config - uses sensible defaults currency = CurrencyConfig( enabled=True, update_interval=3600, # Update rates every hour )

Default values: target_currency="USD", auto_update_enabled=True, update_on_startup=True

What happens automatically:

  • django_cfg.apps.tools.currency added to INSTALLED_APPS
  • CurrencyRate admin registered
  • update_all_rates task added to RQ scheduler
  • Stale rates updated on Django startup

”Display vs Storage Separation”

Use CaseComponentWhat It Does
Store money in modelMoneyFieldCreates price, price_currency fields
Display in admin listMoneyFieldDisplayFormats as ₩50,000,000 → $33,900
Edit in admin formMoneyFieldWidgetAmount input + currency dropdown

Key Components

MoneyField

Composite model field that stores amount and currency:

from django_cfg.modules.django_currency import MoneyField class Vehicle(models.Model): # Creates: price, price_currency, price_target (property) price = MoneyField( max_digits=15, decimal_places=2, default_currency="KRW", target_currency="USD", )

What gets created:

AttributeTypeStorageDescription
priceDecimalFieldDatabaseOriginal amount
price_currencyCharFieldDatabaseCurrency code (e.g., “KRW”)
price_targetPropertyCalculatedConverted amount via CurrencyRate
price_displayPropertyCalculatedFormatted original (e.g., “₩50M”)
price_target_displayPropertyCalculatedFormatted target (e.g., “$33,900”)
price_full_displayPropertyCalculatedCombined (e.g., “$33,900 (₩50M)”)

Learn more about MoneyField →

CurrencyRate Model

Single source of truth for exchange rates:

from django_cfg.apps.tools.currency.models import CurrencyRate # Get rate rate = CurrencyRate.get_rate("KRW", "USD") # Returns: CurrencyRate(base="KRW", quote="USD", rate=0.000678) # Get rate value value = CurrencyRate.get_rate_value("KRW", "USD") # Returns: Decimal("0.000678")

Table structure (~50 rows):

FieldExampleDescription
base_currencyKRWSource currency
quote_currencyUSDTarget currency
rate0.000678Exchange rate
providerhybridRate source
updated_at2025-01-18 12:00Last update

PydanticAdmin (with MoneyFieldAdminMixin)

PydanticAdmin automatically includes MoneyFieldAdminMixin, so no extra mixins needed:

from django_cfg.modules.django_admin.base import PydanticAdmin @admin.register(Vehicle) class VehicleAdmin(PydanticAdmin): config = vehicle_config

Auto-provides:

  • Currency dropdown synced with model
  • Currency saved on form submit
  • Live conversion widget

MoneyFieldDisplay

Compact display field for admin lists. Auto-detects related fields from base name:

from django_cfg.modules.django_admin import MoneyFieldDisplay display_fields = [ # Auto-detects: price_currency, price_target, price_rate, price_rate_at MoneyFieldDisplay( name="price", title="Price", ), ] # Renders: ₩50,000,000 → $33,900

Learn more about MoneyFieldDisplay →


Rate Providers

Fiat Currencies (HybridClient)

Multi-source provider with fallbacks:

SourceCoverageNotes
Fawaz Currency API200+ currenciesPrimary, CDN-backed
Frankfurter APIEUR-based pairsECB data
ExchangeRate-APIUSD-based pairsFree tier
CBR APIRUB pairsRussian Central Bank

Cryptocurrencies (CoinPaprikaClient)

FeatureValue
Coverage100+ top cryptocurrencies
API KeyNot required
SupportedBTC, ETH, SOL, DOGE, USDT, USDC, etc.

Rate Update Flow

Update triggers:

  1. Scheduled: RQ task sync_all runs based on update_interval
  2. On startup: Background thread syncs currencies (if needed) + rates (if stale)
  3. Manual: Management command or admin button

Startup Behavior

When Django starts, a background thread runs sync_all():

Django startup Background thread spawned (non-blocking) sync_currencies_if_needed() → adds missing currencies update_rates_if_needed() → updates stale rates

Non-blocking: Django continues serving requests immediately. Currency sync runs in background.

Management Command

# Update rates only python manage.py update_currency_rates # Sync currencies + rates python manage.py update_currency_rates --all # Show statistics python manage.py update_currency_rates --stats --verbose

When to Use What

ScenarioUse
Store price in modelMoneyField
Display price in admin listMoneyFieldDisplay in display_fields
Edit price in admin formPydanticAdmin (auto-applies widget)
Get rate programmaticallyCurrencyRate.get_rate()
Convert amount in codeCurrencyConverter.convert()
Update rates manuallyAdmin action or update_all_rates()

Quick Example

# models.py from django.db import models from django_cfg.modules.django_currency import MoneyField class Vehicle(models.Model): name = models.CharField(max_length=200) price = MoneyField( default_currency="KRW", target_currency="USD", ) # admin.py from django.contrib import admin from django_cfg.modules.django_admin import AdminConfig, MoneyFieldDisplay from django_cfg.modules.django_admin.base import PydanticAdmin config = AdminConfig( model=Vehicle, list_display=["name", "price"], display_fields=[ # Auto-detects: price_currency, price_target MoneyFieldDisplay( name="price", title="Price", ), ], ) @admin.register(Vehicle) class VehicleAdmin(PydanticAdmin): config = config

See Also

Last updated on