import random
class PrimaryReplicaRouter:
"""Route reads to replicas, writes to primary."""
def db_for_read(self, model, **hints):
"""Direct read queries to a random replica."""
return random.choice(['replica1', 'replica2'])
def db_for_write(self, model, **hints):
"""Direct write queries to primary database."""
return 'default'
def allow_relation(self, obj1, obj2, **hints):
"""Allow relations if both objects are in same db."""
db_set = {'default', 'replica1', 'replica2'}
if obj1._state.db in db_set and obj2._state.db in db_set:
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""Ensure migrations only run on primary."""
return db == 'default'
Database routers direct queries to specific databases. I implement db_for_read(), db_for_write(), allow_relation(), and allow_migrate() methods. This enables read replicas, sharding by model, or separating analytics data. The router checks model labels or app names to route appropriately. For read replicas, I return a random replica in db_for_read() for load balancing. Migrations respect router rules via allow_migrate(). I configure databases in settings and list routers in DATABASE_ROUTERS. This scales Django beyond single-database limitations without changing application code.