ninja-devx¶
Typed controllers and reusable resource policies for Django Ninja. The package centralizes recurring tenant/owner scoping, transaction, permission, dependency-lifetime and error-handling rules while keeping Django models and Ninja schemas.
Read the scope and engineering rationale for adoption criteria, responsibility boundaries and tradeoffs. Version 0.0.2 is an alpha release; the support policy describes the compatibility contract.
Get started See the examples Compare
class ProjectController(
SoftDeleteMixin[Project, ProjectOut], CRUDController[Project, ProjectOut, ProjectIn]
):
tenant_field = "workspace" # every query scoped to the caller's workspace
soft_delete = SoftDelete("archived_at", deleted_by="archived_by")
etag = ETag(field="updated_at") # 304 on reads, If-Match → 412 on writes
search_fields = ("name",)
ordering_fields = ("name", "updated_at")
routes = {"restore": {"path": "/{pk}/unarchive", "permissions": [IsWorkspaceAdmin()]}}
mount(api, {"/projects": ProjectController}, prefix="/v1")
These lines give you list, retrieve, create, update, partial update, archive and unarchive endpoints, with:
- tenant isolation
- search and enum-checked ordering
- conditional requests and optimistic locking
- admin-only unarchiving
- relation loading derived from supported schema fields
- declared errors in the generated OpenAPI schema
-
Controllers on native routers
@get/@poston classes, typed options, scopes, hooks, plugins.as_router()returns aninja.Router; compatibility tests cover the operation integration. -
Reusable CRUD policy
Generic arguments configure filters, search, ordering, cursor pagination, nested resources, bulk writes, soft delete and N+1 prevention.
-
Layers without force
HTTP-free services, repositories, policies and domain errors, when you need them. Three architecture recipes pass the same tests.
-
CQRS without a bus
use_caseanduse_queryhandlers,DomainEventdelivered after commit through an explicitEventBus, andUnitOfWorkfor multi-repository transactions. -
SaaS-ready HTTP
tenant_field,ETag/If-Match, user, scope and tenant throttles, role-based field visibility, and middleware for security headers, request hardening, response caching and pagination headers. -
Async that stays cheap
One class serves sync and async. A unit of work takes one thread hop, and lazy loads and blocking calls fail loudly.
-
Contract and drift checks
Scaffolding with model constraints, schema drift checks, system checks, schemathesis contract tests, and typed TypeScript and pydantic clients.
-
Inspect and diagnose
devx_inspectprints the resolved policy of a mounted controller, and@requires_relatedmakes N+1 loading explicit.
The guides index maps each task to its page, and Controllers, CRUD, Permissions and Errors are the best starting points.
Principles¶
- Native Ninja
- Controllers compile into regular
ninja.Routers. Parsing, validation, auth, pagination, throttling, streaming and exception handlers are Ninja's own. - Typed end to end
- Options are
TypedDicts and generic arguments configure CRUD. mypy strict and pyright strict pass, and the package does not usetyping.Any. - Fail at startup
- Configuration mistakes raise
ControllerConfigErrorfromas_router(), or show up inmanage.py check, not on the first request. - No hidden state
- Request data is never stored on
self, and there are no global registries. - Measured
- Controllers cost microseconds over function views, and an optional local budget check guards that line. See Performance.