django-modern-rest¶
django-modern-rest is built on top of Django’s regular request lifecycle, so the diwire
integration is the same one you use for plain Django views:
diwire.integrations.django.RequestContextMiddlewarestores the currentdjango.http.HttpRequestin acontextvars.ContextVarfor the duration of a request.diwire.integrations.django.add_request_context()registersHttpRequestin yourdiwire.Container, so controller methods and request-scoped services can depend on it.
The package is installed from PyPI as django-modern-rest, but current releases expose their
runtime API from the dmr module.
Minimal setup¶
# settings.py
MIDDLEWARE = [
# ...
"diwire.integrations.django.RequestContextMiddleware",
# ...
]
import django
from django.apps import apps
from django.conf import settings
from django.http import HttpRequest
from django.urls import path
from pydantic import BaseModel
from dmr import Controller
from dmr.plugins.pydantic import PydanticSerializer
from diwire import Container, Injected, Lifetime, Scope, resolver_context
from diwire.integrations.django import add_request_context
if not settings.configured:
settings.configure(
ALLOWED_HOSTS=["*"],
INSTALLED_APPS=[],
SECRET_KEY="docs-example-secret",
)
if not apps.ready:
django.setup()
container = Container()
add_request_context(container)
class PathResponse(BaseModel):
direct_path: str
service_path: str
class RequestPathService:
def __init__(self, request: HttpRequest) -> None:
self._request = request
def path(self) -> str:
return self._request.path
container.add(
RequestPathService,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
class PathController(Controller[PydanticSerializer]):
@resolver_context.inject(scope=Scope.REQUEST)
def get(
self,
request: Injected[HttpRequest],
service: Injected[RequestPathService],
) -> PathResponse:
return PathResponse(
direct_path=request.path,
service_path=service.path(),
)
urlpatterns = [
path("api/path/", PathController.as_view()),
]
Inject HttpRequest in controllers and services¶
django-modern-rest controllers are regular Django View subclasses, so diwire works well
on controller methods decorated with @resolver_context.inject(scope=Scope.REQUEST).
This gives you the same benefits as plain Django integration:
inject the active request directly into controller methods
inject the same request into request-scoped services
Response handling¶
django-modern-rest validates and serializes controller responses. Prefer returning typed data
or using controller helpers like self.to_response(...) / self.to_error(...) instead of
constructing raw Django HttpResponse objects yourself.
Testing¶
For in-process tests, you can use dmr.test.DMRClient (or Django’s normal test client) and
keep the same diwire setup: install RequestContextMiddleware, call
add_request_context(container), and route your controller with .as_view().
from django.test import override_settings
from dmr.test import DMRClient
with override_settings(
ROOT_URLCONF=__name__,
MIDDLEWARE=["diwire.integrations.django.RequestContextMiddleware"],
):
response = DMRClient().get("/api/path/")
assert response.status_code == 200
assert response.json() == {
"direct_path": "/api/path/",
"service_path": "/api/path/",
}
How it works¶
Django receives a request and executes
RequestContextMiddleware.The middleware stores the active
HttpRequestin aContextVar.django-modern-restinstantiates the controller and dispatches the matching endpoint.@resolver_context.inject(scope=Scope.REQUEST)opens a request scope, resolvesInjected[...]parameters, and calls the controller method.After the controller method returns, the injected wrapper ends request-scoped resolution and the middleware resets request context.