Multi-Database Configuration
Automatic Database Routing
Django-CFG provides automatic database routing - no need to manually specify using() on every query. Configure once, and all queries route to the correct database automatically.
Configure multiple databases with automatic routing in Django-CFG.
Basic Multi-Database Setup
Simple Setup (2-3 databases)
Simple Multi-Database Pattern
Perfect for small to medium projects with 2-3 databases.
# config.py
from django_cfg import DjangoConfig
from django_cfg.models import DatabaseConfig
class MyConfig(DjangoConfig):
databases = {
"default": DatabaseConfig(
engine="django.db.backends.postgresql",
name="main_db",
user="postgres",
password="${DB_PASSWORD}",
host="localhost",
),
"blog_db": DatabaseConfig(
engine="django.db.backends.postgresql",
name="blog_db",
user="postgres",
password="${DB_PASSWORD}",
host="localhost",
),
}
database_routers = ["django_cfg.routing.routers.AppDatabaseRouter"]
# Routing rules
database_routing_rules = {
"blog": "blog_db", # Blog app → blog_db
}Simple Pattern Benefits
- ✅ Easy to understand - Clear database definitions
- ✅ Type-safe - Full IDE autocomplete
- ✅ Automatic routing - No manual
using()calls - ✅ Environment variables - Secure credential management
Advanced Setup (4+ databases)
Advanced Multi-Database Pattern
For large applications with multiple domains and services.
# config.py
from django_cfg import DjangoConfig
from django_cfg.models import DatabaseConfig
class MyConfig(DjangoConfig):
databases = {
# Core databases
"default": DatabaseConfig(
engine="django.db.backends.postgresql",
name="main",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_HOST}",
conn_max_age=600, # Connection pooling
),
# Domain databases
"blog_db": DatabaseConfig(
engine="django.db.backends.postgresql",
name="blog",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_BLOG_HOST}",
conn_max_age=600,
),
"shop_db": DatabaseConfig(
engine="django.db.backends.postgresql",
name="shop",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_SHOP_HOST}",
conn_max_age=600,
),
# Analytics - read-only replica
"analytics": DatabaseConfig(
engine="django.db.backends.postgresql",
name="analytics",
user="${DB_READONLY_USER}",
password="${DB_READONLY_PASSWORD}",
host="${DB_ANALYTICS_HOST}",
options={"connect_timeout": 10, "options": "-c default_transaction_read_only=on"},
),
}
database_routers = ["django_cfg.routing.routers.AppDatabaseRouter"]
database_routing_rules = {
"blog": "blog_db",
"shop": "shop_db",
"analytics": "analytics",
}Advanced Pattern Benefits
- ✅ Scalability - Separate databases per domain
- ✅ Connection pooling - Better performance
- ✅ Read replicas - Analytics on separate database
- ✅ Per-database hosts - Distributed architecture
Microservices Pattern
Microservices Database Pattern
Each service has its own database with shared authentication.
# config.py
from django_cfg import DjangoConfig
from django_cfg.models import DatabaseConfig
class MyConfig(DjangoConfig):
databases = {
# Shared auth database
"default": DatabaseConfig(
engine="django.db.backends.postgresql",
name="auth_service",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_AUTH_HOST}",
),
# Blog microservice
"blog_service": DatabaseConfig(
engine="django.db.backends.postgresql",
name="blog_service",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_BLOG_HOST}",
),
# Shop microservice
"shop_service": DatabaseConfig(
engine="django.db.backends.postgresql",
name="shop_service",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_SHOP_HOST}",
),
# Payment microservice
"payment_service": DatabaseConfig(
engine="django.db.backends.postgresql",
name="payment_service",
user="${DB_USER}",
password="${DB_PASSWORD}",
host="${DB_PAYMENT_HOST}",
),
}
database_routers = ["django_cfg.routing.routers.AppDatabaseRouter"]
database_routing_rules = {
"blog": "blog_service",
"shop": "shop_service",
"payments": "payment_service",
}Microservices Considerations Benefits:
- ✅ Service isolation - Each service has independent database
- ✅ Independent scaling - Scale databases separately
- ✅ Technology diversity - Different DB engines per service
Challenges:
- ⚠️ Cross-service queries - Must use
db_constraint=Falsefor FKs - ⚠️ Distributed transactions - No ACID across services
- ⚠️ Data consistency - Eventual consistency patterns needed
- ⚠️ Testing complexity - Multiple databases to seed
YAML Configuration
# config.yaml
database:
url: "postgresql://user:${DB_PASSWORD}@localhost:5432/main_db"
url_blog: "postgresql://user:${DB_PASSWORD}@localhost:5432/blog_db"
url_shop: "postgresql://user:${DB_PASSWORD}@localhost:5432/shop_db"URL Format Examples
SQLite (Development):
database:
url: "sqlite:///db/default.sqlite3"
url_blog: "sqlite:///db/blog.sqlite3"PostgreSQL (Production):
database:
url: "postgresql://user:[email protected]:5432/main"
url_blog: "postgresql://user:[email protected]:5432/blog"With SSL:
database:
url: "postgresql://user:pass@localhost/main?sslmode=require"Database Routing
Django-CFG automatically routes database operations based on DATABASE_ROUTING_RULES:
# settings.py
DATABASE_ROUTING_RULES = {
'blog': 'blog_db', # All blog app models → blog_db
'shop': 'shop_db', # All shop app models → shop_db
# Apps not listed → default database
}Automatic Routing
# models.py - apps/blog/models.py
class Post(models.Model):
title = models.CharField(max_length=200)
# No need to specify database!
# Usage - automatically routed to blog_db
Post.objects.create(title="Hello")
Post.objects.all() # Queries blog_db automaticallyCross-Database ForeignKeys
CRITICAL: db_constraint=False Required
When creating ForeignKeys to models in different databases (e.g., User in default, Post in blog_db), you MUST use db_constraint=False. This is required because database constraints cannot span multiple databases.
Implementation
# apps/blog/models.py
from django.contrib.auth import get_user_model
User = get_user_model() # Lives in 'default' database
class Post(models.Model):
title = models.CharField(max_length=200)
# Cross-database ForeignKey
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='blog_posts',
db_constraint=False # ⚠️ REQUIRED for cross-database FK
)
class Meta:
db_table = 'blog_post'Why db_constraint=False?
Explanation
Technical reasons:
- User model is in
defaultdatabase - Post model is in
blog_dbdatabase - Database-level foreign key constraints cannot span databases
- Django ORM handles the relationship at application level
- Without it:
relation "auth_user" does not existerror during migrations
How it works:
- Django stores only the foreign key value (
author_id=1) - No database constraint created
- Relationship resolved at application level when accessed
post.authortriggers ORM query to default database
Trade-offs
Benefits:
- ✅ Cross-database relationships work - Link models across databases
- ✅ Django ORM validates relationships -
post.author,user.blog_postswork - ✅ Automatic routing - ORM queries correct database for each model
- ✅ Select/prefetch works - Can optimize queries with
select_related()
Limitations:
- ❌ No database-level referential integrity - Database won’t enforce constraint
- ❌ No automatic cascade deletes at DB level - Django handles in Python
- ❌ Orphaned records possible - If you delete directly in database
- ❌ Performance overhead - Relationship validation happens in Python
Best Practices
✅ Do:
# Use db_constraint=False for cross-database FKs
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
db_constraint=False, # ✅ Required
)
# Use Django ORM for all deletes
user.delete() # ✅ Cascades to related posts in blog_db❌ Don’t:
# Don't omit db_constraint=False
author = models.ForeignKey(User) # ❌ Will fail during migration
# Don't delete directly in database
# DELETE FROM auth_user WHERE id=1; # ❌ Orphans posts in blog_db
# Don't use database-level cascades
# They won't work across databasesValidation strategy:
# Add application-level validation
class Post(models.Model):
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
db_constraint=False,
)
def clean(self):
# Validate author exists
if not User.objects.filter(id=self.author_id).exists():
raise ValidationError("Author does not exist")Migration Commands
Django-CFG provides two commands for multi-database migrations:
migrate_all (Recommended for Production)
Automatically migrates all databases:
# Migrate all databases
python manage.py migrate_all
# Skip automatic makemigrations
python manage.py migrate_all --skip-makemigrationsFeatures:
- Auto-detects all databases
- Creates migrations first
- Migrates each database based on routing rules
- Handles constance app automatically
migrator (Recommended for Development)
Interactive migration tool with auto mode:
# Auto mode - migrates all databases without prompts
python manage.py migrator --auto
# Interactive mode - shows menu
python manage.py migrator
# Migrate specific database
python manage.py migrator --database blog_db
# Migrate specific app
python manage.py migrator --app blogSee: Core Commands for detailed migrator documentation.
Complete Production Example
# config.prod.yaml
database:
# Main database
url: "postgresql://user:${DB_PASSWORD}@db-main.example.com:5432/main?sslmode=require&connect_timeout=10"
# Domain databases
url_blog: "postgresql://user:${DB_PASSWORD}@db-blog.example.com:5432/blog?sslmode=require"
url_shop: "postgresql://user:${DB_PASSWORD}@db-shop.example.com:5432/shop?sslmode=require"
# Analytics - read-only
url_analytics: "postgresql://readonly:${DB_READONLY_PASSWORD}@warehouse.example.com:5432/analytics?sslmode=require"# settings.py
import dj_database_url
from django_cfg import load_config
config = load_config()
DATABASES = {
'default': dj_database_url.parse(config.database.url),
'blog_db': dj_database_url.parse(config.database.url_blog),
'shop_db': dj_database_url.parse(config.database.url_shop),
'analytics': dj_database_url.parse(config.database.url_analytics),
}
DATABASE_ROUTERS = ['django_cfg.routing.DatabaseRouter']
DATABASE_ROUTING_RULES = {
'blog': 'blog_db',
'shop': 'shop_db',
'analytics': 'analytics',
}Testing Multi-Database Setup
# Create migrations
python manage.py makemigrations
# Migrate all databases
python manage.py migrate_all
# Or use migrator in auto mode
python manage.py migrator --auto
# Test routing in shell
python manage.py shell# Shell - test automatic routing
from apps.blog.models import Post
from django.contrib.auth import get_user_model
User = get_user_model()
# Create user in default database
user = User.objects.create_user(username='john', email='[email protected]')
# Create post in blog_db with cross-database FK
post = Post.objects.create(
title='Hello World',
content='Multi-database setup works!',
author=user # Cross-database FK
)
# Test relationships
print(post.author.username) # 'john'
print(user.blog_posts.all()) # QuerySet with postSee Also
Database Configuration
Core Database Setup:
- Database Configuration - Complete database configuration guide
- Database Routing - Advanced routing strategies and patterns
- Configuration Models - Database config API reference
Infrastructure:
- Cache Configuration - Redis for multi-database caching
- Environment Detection - Database per environment
- Docker Deployment - Multi-database in Docker
Configuration & Setup
Getting Started:
- Installation - Install Django-CFG
- Configuration Guide - YAML configuration setup
- First Project - Quick start with database
Production:
- Production Config - Production database setup
- Environment Variables - Secure database credentials
- Type-Safe Configuration - Database config validation
Examples & Guides
Practical Examples:
- Sample Project - Multi-database example project
- Sample Project Multi-DB - Detailed multi-database setup
- Examples Guide - Real-world database patterns
Related Guides:
- Migration Guide - Migrate existing multi-database projects
- Troubleshooting - Common database issues
Tools & Operations
CLI & Management:
- CLI Tools - Database management commands
- Core Commands - Migration and database commands
Background Processing:
- ReArq Integration - Task routing per database
- Background Tasks - Database-aware tasks
Note: All examples use YAML-based configuration via env loader. See Configuration Guide for setup.