Skip to content

State manager (preferred)

How does it work

Sequence diagram simplified Sequence diagram simplified

Simplified sequence diagram. See the full version of the diagram.

When you execute the Django project using the ASGI server (like uvicorn), it sends lifespan events at its startup. These lifespan events are ignored by the standard Django ASGI handler. The lifespan events include the lifespan scope state, which is a Python dictionary that is preserved by the ASGI server and allowed to be modified by the developer. Hence, it's a suitable location for storing global application states or shared objects. For instance, one could create a shared HTTPX async client to implement connection pooling.

Accessing the shared state

To access the shared state within a view, your project must include middleware. This middleware assigns a new attribute to the request object, using the state it receives from the ASGI server.

1
2
3
4
5
MIDDLEWARE = [
    # ...
    'django_asgi_lifespan.middleware.LifespanStateMiddleware',
    # ...
]

Context manager

You must define an async context manager. The code for this can be placed anywhere; in this example, the function is defined in the context.py file. The code up to the yield statement is executed when the ASGI server starts, and the remaining part is executed when the server shuts down.

context.py
from contextlib import asynccontextmanager

import httpx

from django_asgi_lifespan.types import State


@asynccontextmanager
async def httpx_lifespan_manager() -> State:
    state = {
        "httpx_client": httpx.AsyncClient()
    }

    try:
        yield state
    finally:
        await state["httpx_client"].aclose()

Registering the context manager

The manager that you have just defined needs to be registered. The most suitable location for registration is Django AppConfig.

apps.py
from django.apps import AppConfig

from django_asgi_lifespan.register import register_lifespan_manager
from .context import (
    httpx_lifespan_manager,
)


class ExampleAppConfig(AppConfig):

    def ready(self):
        register_lifespan_manager(
            context_manager=httpx_lifespan_manager
        )

View

After the above steps, you can access the shared state in the views.

views.py
from http import HTTPStatus

import httpx
from django.http import HttpResponse


async def example_view(request) -> HttpResponse:
    httpx_client: httpx.AsyncClient = request.state["httpx_client"]

    await httpx_client.head("https://www.example.com/")

    return HttpResponse(
        "OK",
        status=HTTPStatus.OK,
        content_type="text/plain; charset=utf-8",
    )

Sequence diagram

Sequence diagram Sequence diagram

Sequence diagram. Please note that the error handling (lifespan.startup.failed, lifespan.shutdown.failed) is not documented in the diagram.