Skip to main content

Django database read-write separation router with transaction-aware and consistent hash routing support

Project description

Django Read-Write Router

Django database read-write separation router with transaction-aware and consistent hash routing support.

Features

  • Basic Read-Write Separation: Route writes to primary, reads to replicas
  • Transaction-Aware Routing: Automatically route reads to primary when in a transaction
  • Consistent Hash Routing: Same user always reads from same replica (monotonic reads)
  • Request Context Tracking: Automatic context management via middleware
  • Primary-Only QuerySet: Force specific queries to use primary database

Installation

pip install django-rw-router

Or with uv:

uv add django-rw-router

Quick Start

1. Configure Databases

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'primary.db.example.com',
        'PORT': '5432',
    },
    'readonly': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'readonly_user',
        'PASSWORD': 'password',
        'HOST': 'replica.db.example.com',
        'PORT': '5432',
    },
}

2. Add Router

# settings.py
DATABASE_ROUTERS = [
    'django_rw_router.routers.TransactionPrimaryReplicaRouter',
]

3. Add Middleware (Optional)

# settings.py
MIDDLEWARE = [
    'django_rw_router.middleware.RequestContextMiddleware',
    # ... other middleware
]

Router Options

PrimaryReplicaRouter

Basic read-write separation. Writes go to primary, reads are randomly distributed among replicas.

DATABASE_ROUTERS = [
    'django_rw_router.routers.PrimaryReplicaRouter',
]

TransactionPrimaryReplicaRouter (Recommended)

Extends basic router with transaction awareness. Reads inside a transaction go to primary.

DATABASE_ROUTERS = [
    'django_rw_router.routers.TransactionPrimaryReplicaRouter',
]

HashPrimaryReplicaRouter

Uses consistent hashing based on user_id from RequestContext. Same user always reads from same replica.

DATABASE_ROUTERS = [
    'django_rw_router.routers.hash.HashPrimaryReplicaRouter',
]

Configuration

Database Aliases

# settings.py

# Read replica aliases (single or list)
DJANGO_RW_ROUTER_READ_DBS = ['readonly', 'readonly2']

# Write database alias
DJANGO_RW_ROUTER_WRITE_DB = 'default'

# Hash ring virtual nodes (for HashPrimaryReplicaRouter)
DJANGO_RW_ROUTER_HASH_VIRTUAL_NODES = 40

Middleware Configuration

# settings.py

# How to extract user_id from request (nested attributes supported)
DJANGO_RW_ROUTER_USER_ID_ATTR = 'user.id'  # Default

# How to extract request_id from request
DJANGO_RW_ROUTER_REQUEST_ID_ATTR = 'id'  # Default

QuerySet Configuration

# settings.py

# Force all PrimaryQuerySet reads to use primary
DJANGO_RW_ROUTER_QUERYSET_USING_ENABLE = True

# Enable @use_primary_db decorator for specific methods
DJANGO_RW_ROUTER_METHOD_USING_ENABLE = True

Usage Examples

Basic Usage

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)

# Writes go to 'default', reads go to 'readonly'
book = Book.objects.create(title="Django Guide")  # Write to primary
books = Book.objects.all()  # Read from replica

Transaction-Aware Reads

from django.db import transaction

with transaction.atomic():
    book = Book.objects.create(title="New Book")
    # This read goes to PRIMARY (not replica) because we're in a transaction
    fresh_book = Book.objects.get(id=book.id)

Using PrimaryManager for Critical Queries

from django.db import models
from django_rw_router.managers import PrimaryManager

class ImportantModel(models.Model):
    objects = PrimaryManager()

# When DJANGO_RW_ROUTER_QUERYSET_USING_ENABLE=True,
# all reads through this manager go to primary
obj = ImportantModel.objects.first()

Manual Database Selection

You can always override the router:

# Force read from primary
obj = Book.objects.using('default').first()

# Force write to replica (not recommended)
book.save(using='readonly')

How It Works

  1. Write Operations: Always routed to the primary database (default)
  2. Read Operations:
    • Outside transaction: Routed to random read replica
    • Inside transaction: Routed to primary (prevents stale reads)
  3. Hash Routing: user_id is hashed to consistently select the same replica
  4. Context Tracking: Middleware sets/clears request context automatically

Requirements

  • Python >= 3.8
  • Django >= 3.2
  • uhashring >= 2.1

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_rw_router-0.0.1.tar.gz (56.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_rw_router-0.0.1-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file django_rw_router-0.0.1.tar.gz.

File metadata

  • Download URL: django_rw_router-0.0.1.tar.gz
  • Upload date:
  • Size: 56.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for django_rw_router-0.0.1.tar.gz
Algorithm Hash digest
SHA256 fb409f4df01d6675cd9baf36966d305a124b734ff960eb04addaa8a04d66873a
MD5 c617f8ac7b42d667e618e11d27cbeaa0
BLAKE2b-256 44afdbeb3a5be03e700f7d135f1e1aca945d725be9cb4fd8b5f35c9e75c66b64

See more details on using hashes here.

File details

Details for the file django_rw_router-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_rw_router-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 62c5600d5545669968bf62b092ddf72676cd024f054b7818bcaa4689eb3d0945
MD5 14ad9dc58f85b9181705d782402832b4
BLAKE2b-256 62dbeeb3117747e7f5a0622dc2be608af43f531cc78fd59637df0d1d3c187908

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page