dioxide.django¶
Django integration for dioxide dependency injection.
This module provides seamless integration between dioxide’s dependency injection container and Django applications. It enables:
Single function setup:
configure_dioxide(profile=...)Request scoping: Automatic
ScopedContainerper HTTP request via middlewareClean injection:
inject(Type)resolves from current request scopeLifecycle management: Container start/stop tied to Django configuration
- Quick Start:
Set up dioxide in your Django settings.py or apps.py:
# In settings.py or your AppConfig.ready() from dioxide import Profile from dioxide.django import configure_dioxide configure_dioxide(profile=Profile.PRODUCTION)
Add the middleware to settings.py:
MIDDLEWARE = [ ... 'dioxide.django.DioxideMiddleware', ... ]
Use in views:
from dioxide.django import inject def my_view(request): service = inject(UserService) return JsonResponse(service.get_data())
- Request Scoping:
The middleware creates a
ScopedContainerfor each HTTP request. This enables REQUEST-scoped components to be shared within a single request but fresh for each new request:from dioxide import service, Scope @service(scope=Scope.REQUEST) class RequestContext: def __init__(self): import uuid self.request_id = str(uuid.uuid4()) # In views: def my_view(request): ctx = inject(RequestContext) # ctx.request_id is unique per request # but shared if resolved multiple times within same request return JsonResponse({'request_id': ctx.request_id})
- Lifecycle Management:
The integration handles container lifecycle automatically:
from dioxide import adapter, lifecycle, Profile @adapter.for_(DatabasePort, profile=Profile.PRODUCTION) @lifecycle class PostgresAdapter: async def initialize(self) -> None: self.engine = create_engine(...) print('Database connected') async def dispose(self) -> None: await self.engine.dispose() print('Database disconnected') # When configure_dioxide is called: container.scan() and start() # When request ends: scope.dispose() for REQUEST-scoped components
- Thread Safety:
Django uses threading by default. The integration stores the scoped container in thread-local storage, ensuring each request gets its own scope even in threaded mode.
See also
configure_dioxide()- The main setup functionDioxideMiddleware- Request scoping middlewareinject()- Dependency injection helper for viewsdioxide.container.Container- The DI containerdioxide.container.ScopedContainer- Request-scoped container
Classes¶
Django middleware that creates a ScopedContainer per request. |
Functions¶
|
Configure dioxide dependency injection for a Django application. |
|
Resolve a component from the current request's dioxide scope. |
Module Contents¶
- dioxide.django.configure_dioxide(profile=None, container=None, packages=None)[source]¶
Configure dioxide dependency injection for a Django application.
This function sets up the integration between dioxide and Django:
Scans for components in specified packages (or all registered)
Starts the container (initializing @lifecycle components)
Stores the container reference for later access by middleware
Call this in your Django settings.py, apps.py ready(), or conftest.py.
- Parameters:
profile (dioxide.profile_enum.Profile | str | None) – Profile to scan with (e.g.,
Profile.PRODUCTION). Accepts either a Profile enum value or a string profile name.container (dioxide.container.Container | None) – Optional Container instance. If not provided, uses the global
dioxide.containersingleton.packages (list[str] | None) – Optional list of packages to scan for components. If not provided, scans all registered components.
- Raises:
ImportError – If Django is not installed.
- Return type:
None
Example
Basic setup in settings.py:
from dioxide import Profile from dioxide.django import configure_dioxide configure_dioxide(profile=Profile.PRODUCTION)
In apps.py ready() method:
from django.apps import AppConfig from dioxide import Profile from dioxide.django import configure_dioxide class MyAppConfig(AppConfig): name = 'myapp' def ready(self): configure_dioxide(profile=Profile.PRODUCTION)
With custom container:
from dioxide import Container, Profile from dioxide.django import configure_dioxide my_container = Container() configure_dioxide(profile=Profile.TEST, container=my_container)
With package scanning:
configure_dioxide( profile=Profile.PRODUCTION, packages=['myapp.services', 'myapp.adapters'], )
See also
DioxideMiddleware- Must be added to MIDDLEWAREinject()- How to inject dependencies in viewsdioxide.container.ScopedContainer- How scoping works
- class dioxide.django.DioxideMiddleware(get_response)[source]¶
Django middleware that creates a ScopedContainer per request.
This middleware handles request scoping for dioxide:
Creates a
ScopedContainerbefore the view runsStores it in thread-local storage for
inject()to accessDisposes the scope after the response is returned
Usage in settings.py:
MIDDLEWARE = [ ... 'dioxide.django.DioxideMiddleware', ... ]
Note
The middleware must be placed after any middleware that might need dioxide services, as it creates the scope on request entry.
See also
configure_dioxide()- Must be called firstinject()- How to inject dependencies in viewsdioxide.container.ScopedContainer- The scoped container
- Parameters:
get_response (collections.abc.Callable[[django.http.HttpRequest], django.http.HttpResponse])
- __call__(request)[source]¶
Process a request with dioxide scoping.
Creates a scoped container for the request, stores it in thread-local storage, calls the view, and ensures cleanup on completion.
- Parameters:
request (django.http.HttpRequest) – The Django HttpRequest object.
- Returns:
The HttpResponse from the view.
- Return type:
django.http.HttpResponse
- dioxide.django.inject(component_type)[source]¶
Resolve a component from the current request’s dioxide scope.
This function retrieves a dependency from the dioxide container for the current request. It automatically uses the correct scope:
SINGLETON: Resolved from parent container (shared)
REQUEST: Resolved from request scope (fresh per request)
FACTORY: New instance each resolution
- Parameters:
component_type (type[T]) – The type to resolve from the container
- Returns:
An instance of the requested type
- Raises:
RuntimeError – If called outside a request context
RuntimeError – If called without
configure_dioxide()being set upImportError – If Django is not installed
- Return type:
T
Example
Basic usage:
from dioxide.django import inject def my_view(request): service = inject(UserService) return JsonResponse(service.get_data())
Multiple dependencies:
def dashboard_view(request): users = inject(UserService) analytics = inject(AnalyticsService) return JsonResponse( { 'users': users.count(), 'visits': analytics.total_visits(), } )
Request-scoped dependencies:
from dioxide import service, Scope @service(scope=Scope.REQUEST) class RequestContext: def __init__(self): self.request_id = str(uuid.uuid4()) def my_view(request): ctx = inject(RequestContext) # ctx is unique per request return JsonResponse({'request_id': ctx.request_id})
Note
Unlike FastAPI’s
Inject()which returns a Depends wrapper, Django’sinject()directly returns the resolved instance. This is because Django doesn’t have a dependency injection system like FastAPI’s Depends.See also
configure_dioxide()- Must be called firstDioxideMiddleware- Must be added to MIDDLEWAREdioxide.container.ScopedContainer- How scoping works