dioxide.ninja¶
Django Ninja integration for dioxide dependency injection.
This module provides seamless integration between dioxide’s dependency injection container and Django Ninja applications. It enables:
Single function setup:
configure_dioxide(api, 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 Ninja app:
from ninja import NinjaAPI from dioxide import Profile from dioxide.ninja import configure_dioxide, inject api = NinjaAPI() configure_dioxide(api, profile=Profile.PRODUCTION) @api.get('/users/me') def get_me(request): ctx = inject(RequestContext) return {'request_id': str(ctx.request_id)} @api.get('/users') def list_users(request): service = inject(UserService) return service.list_all()
Add the middleware to your Django settings:
MIDDLEWARE = [ ... 'dioxide.ninja.DioxideMiddleware', ... ]
- 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 route handlers: @api.get('/test') def test(request): ctx = inject(RequestContext) # ctx.request_id is unique per request # but shared if resolved multiple times within same request return {'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.
Note
Unlike FastAPI which has built-in dependency injection via Depends(),
Django Ninja uses Django’s request handling model. This integration follows
the same pattern as dioxide.django, using middleware for request scoping
and inject() for resolving dependencies inside route handlers.
See also
configure_dioxide()- The main setup functionDioxideMiddleware- Request scoping middlewareinject()- Dependency injection helper for route handlersdioxide.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 Ninja application. |
|
Resolve a component from the current request's dioxide scope. |
Module Contents¶
- dioxide.ninja.configure_dioxide(api, profile=None, container=None, packages=None)[source]¶
Configure dioxide dependency injection for a Django Ninja application.
This function sets up the integration between dioxide and Django Ninja:
Scans for components in specified packages (or all registered)
Starts the container (initializing @lifecycle components)
Stores the container reference for later access by middleware and inject()
Call this during Django application startup (settings.py, apps.py ready(), or wherever you create your NinjaAPI instance).
- Parameters:
api (Any) – The NinjaAPI instance to configure. Currently unused but included for API consistency and potential future use.
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 Ninja is not installed.
- Return type:
None
Example
Basic setup:
from ninja import NinjaAPI from dioxide import Profile from dioxide.ninja import configure_dioxide api = NinjaAPI() configure_dioxide(api, profile=Profile.PRODUCTION)
With custom container:
from dioxide import Container, Profile from dioxide.ninja import configure_dioxide my_container = Container() api = NinjaAPI() configure_dioxide(api, profile=Profile.TEST, container=my_container)
With package scanning:
configure_dioxide( api, profile=Profile.PRODUCTION, packages=['myapp.services', 'myapp.adapters'], )
Note
You must also add
DioxideMiddlewareto your DjangoMIDDLEWAREsetting for request scoping to work.See also
DioxideMiddleware- Must be added to MIDDLEWAREinject()- How to inject dependencies in route handlersdioxide.container.ScopedContainer- How scoping works
- class dioxide.ninja.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.ninja.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 route handlersdioxide.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.ninja.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 Ninja is not installed
- Return type:
T
Example
Basic usage:
from dioxide.ninja import inject @api.get('/users') def list_users(request): service = inject(UserService) return service.list_all()
Multiple dependencies:
@api.get('/dashboard') def dashboard(request): users = inject(UserService) analytics = inject(AnalyticsService) return { '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()) @api.get('/test') def test(request): ctx = inject(RequestContext) # ctx is unique per request return {'request_id': ctx.request_id}
Note
This function uses
inject()(lowercase) to match Django’s naming conventions and the existingdioxide.djangointegration. This is different from FastAPI’sInject()pattern because Django Ninja doesn’t have built-in dependency injection like FastAPI’sDepends.See also
configure_dioxide()- Must be called firstDioxideMiddleware- Must be added to MIDDLEWAREdioxide.container.ScopedContainer- How scoping works