Skip to content

Django Quickstart

Protect a Django view with Baldur. No Redis, no Docker, no environment variables. The in-memory fallback covers the whole first run.

Supports Python 3.11–3.13 and Django 4.2 / 5.2 LTS / 6.x. Assumes you have built a Django app before.

1. Install

pip install baldur-framework[django]

2. Add Baldur to your settings

Add baldur.adapters.django to INSTALLED_APPS. Its app config calls baldur.init() on startup for you — there is nothing else to wire.

"""Minimal Django settings for the Baldur quickstart.

Zero infrastructure: in-memory SQLite + Baldur's in-memory fallback (no Redis,
no env vars). Adding ``baldur.adapters.django`` to ``INSTALLED_APPS`` calls
``baldur.init()`` automatically on startup via the app's ``ready()`` hook.

Production: see the "Add Redis for production" appendix in
``docs/getting-started/django.md`` — the in-memory fallback is single-process
only and is NOT safe for multi-worker deployments.
"""

from __future__ import annotations

SECRET_KEY = "quickstart-insecure-key-do-not-use-in-production"  # noqa: S105
DEBUG = True
ALLOWED_HOSTS = ["*"]

INSTALLED_APPS = [
    "django.contrib.contenttypes",
    "django.contrib.auth",
    # Baldur Django integration — calls baldur.init() on startup.
    "baldur.adapters.django",
]

ROOT_URLCONF = "urls"

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": ":memory:",
    }
}

USE_TZ = True

3. Protect a view

@baldur.protected("demo") wraps the view in Baldur's composed resilience pipeline (circuit breaker on by default):

"""Minimal Django view protected by Baldur's marquee facade.

``@baldur.protected("demo")`` wraps the view in Baldur's composed resilience
pipeline (circuit breaker on by default). With zero configuration it uses the
in-memory fallback — no Redis, no env vars. See
``docs/getting-started/django.md`` for the 5-minute walkthrough.
"""

from __future__ import annotations

from django.http import HttpRequest, JsonResponse

import baldur


@baldur.protected("demo")
def demo(request: HttpRequest) -> JsonResponse:
    """Return a JSON payload through Baldur's resilience pipeline."""
    return JsonResponse({"status": "ok", "service": "demo"})

Route it:

"""URL configuration for the Baldur Django quickstart."""

from __future__ import annotations

from django.urls import path
from views import demo

urlpatterns = [
    path("demo/", demo),
]

4. Run it

Start the dev server and call the route:

python manage.py runserver
curl http://127.0.0.1:8000/demo/
# {"status": "ok", "service": "demo"}

That's it. The response just travelled through a circuit breaker.

See Baldur's events

Baldur logs to stdout automatically. Raise the log level to watch circuit breaker and rate-limit events as you exercise the endpoint:

export BALDUR_LOG_LEVEL=INFO   # circuit opened/closed, rate-limit blocks, ...

Verify without a browser

The quickstart ships a smoke test that drives the view through Django's in-process test client — no server, no infra:

pytest examples/quickstart_django/test_smoke.py

Browse the full runnable app: examples/quickstart_django/.

Going to production

The in-memory fallback is single-process only

The zero-config path uses Baldur's in-memory cache. It keeps state in a per-process store, so copying this quickstart into a multi-worker deployment (gunicorn --workers N, uvicorn --workers N) does not degrade gracefully: idempotency keys, rate-limit counters, and circuit breaker state diverge silently per worker. That breaks correctness, not just scale. The in-memory store also grows unbounded. This is a hazard, not a tuning knob: give Baldur a shared backend before you run more than one worker.

Point Baldur at Redis so all workers share state. No code changes needed: set one environment variable before starting the server:

pip install baldur-framework[django,redis]
export BALDUR_REDIS_URL=redis://localhost:6379/0

That is the only addition the production path needs over the quickstart path.