Skip to content

Django

Django is a Python web framework with built-in ORM and database routing.

Connection settings

Disable SSL and server-side cursors. Server-side cursors use DECLARE ... CURSOR, which holds state across statements — incompatible with transaction-mode pooling where the server connection may change between statements.

settings.py
python
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST": "halephant",
        "PORT": 6432,
        "OPTIONS": {"sslmode": "disable"},
        "DISABLE_SERVER_SIDE_CURSORS": True,
        # ...
    },
}

Read replica routing

Django routes reads and writes through its DATABASE_ROUTERS system. Point both connections at halephant with different users:

settings.py
python
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST": "halephant",
        "PORT": 6432,
        "NAME": "myapp",
        "USER": "myapp",
        "PASSWORD": "...",
        "OPTIONS": {"sslmode": "disable"},
        "DISABLE_SERVER_SIDE_CURSORS": True,
    },
    "replica": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST": "halephant",
        "PORT": 6432,
        "NAME": "myapp",
        "USER": "myapp_ro",
        "PASSWORD": "...",
        "OPTIONS": {"sslmode": "disable"},
        "DISABLE_SERVER_SIDE_CURSORS": True,
    },
}

Define a router:

routers.py
python
class ReadReplicaRouter:
    def db_for_read(self, model, **hints):
        return "replica"

    def db_for_write(self, model, **hints):
        return "default"

    def allow_relation(self, obj1, obj2, **hints):
        return True

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        return db == "default"

Register it in settings:

settings.py
python
DATABASE_ROUTERS = ["myapp.routers.ReadReplicaRouter"]

Django doesn't send BEGIN READ ONLY or any in-band read-only signal. The separate user approach lets halephant handle routing transparently. See the read replica guide for halephant configuration.