Request Lifecycle Patterns¶
Wireup injection in Django is not limited to views. You can also inject services in request-time helpers such as custom decorators and middleware-adjacent functions.
Composable Request Decorators¶
Use @inject to build reusable decorators that run inside the request lifecycle.
from functools import wraps
from django.http import HttpRequest, HttpResponseForbidden
from wireup import Injected
from wireup.integration.django import inject
from myapp.services import BillingService
def require_plan(plan: str):
def decorator(view_fn):
@wraps(view_fn)
@inject
def wrapped(
request: HttpRequest,
*args,
billing: Injected[BillingService],
**kwargs,
):
if not billing.has_plan(request.user.id, plan):
return HttpResponseForbidden("Upgrade required")
return view_fn(request, *args, **kwargs)
return wrapped
return decorator
@require_plan("pro")
def analytics_view(request: HttpRequest): ...
Note
The require_plan example above is sync-only. If your target view is async, use an async decorator variant and
await the wrapped view function.
Middleware-Adjacent Request Hooks¶
Django middleware itself does not use Django's view signature injection. For request-time middleware hooks, call
helpers that use @inject or direct request-container access.
from django.http import HttpRequest
from wireup import Injected
from wireup.integration.django import inject
from myapp.services import RequestContextService
@inject
def initialize_request_context(
request: HttpRequest,
context_service: Injected[RequestContextService],
) -> None:
context_service.initialize(request)
class RequestContextMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
initialize_request_context(request)
return self.get_response(request)
Warning
Ensure wireup.integration.django.wireup_middleware appears before middleware that calls injected helpers.
Otherwise request-scoped injection is not available yet.
Direct Container Access¶
When needed, access containers directly:
from wireup.integration.django import get_app_container, get_request_container
# Current request-scoped container (available only during a request)
request_container = get_request_container()
# Application-wide container
app_container = get_app_container()
Prefer @inject for request-time helpers and @inject_app for non-request entry points.
Testing Lifecycle Helpers¶
See Django Testing for endpoint tests and dependency overrides. These patterns are tested the same way:
- call endpoints with
ClientorAsyncClient - use
get_app_container().override.injectable(...)to inject fakes