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.2.tar.gz (56.0 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.2-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: django_rw_router-0.0.2.tar.gz
  • Upload date:
  • Size: 56.0 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.2.tar.gz
Algorithm Hash digest
SHA256 17a07514e514b30f4f617bab69974794d835c6d2398e638d62621682e105f543
MD5 13f2594ccdb54b9802fd87507559e498
BLAKE2b-256 b3a1df9f2a40e7e6bc50fd5225afb56be6a5c884717f18aae890222d6cac488b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for django_rw_router-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 387fa89baec5298b0fdd476541e35188948d9cb692b8e902cd6a5ae393ab68a4
MD5 2b75b2a3ab697dd8fd42eabccabd2ff9
BLAKE2b-256 985cd46178e3406589c076b5633c65086ab1063b93993261cfd72d8bd2a810c4

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