Commands and checks¶
Management commands, the unasync tool and system checks. See Code generation and System checks.
manage.py devx_scaffold¶
Generate typed schemas, a CRUD controller and tests for a model.
| Argument | Type | Default | Description |
|---|---|---|---|
model |
value | — | app_label.ModelName, e.g. blog.Article |
--check |
flag | — | report drift between models and the schemas of mounted controllers |
--output |
value | — | module file (default: |
--tests |
value | — | test file (default: |
--read |
list | — | fields of the output schema |
--write |
list | — | fields of the input schema |
--owner |
value | — | foreign key to the user (sets owner_field) |
--async |
flag | — | generate async operations |
--no-tests |
flag | — | do not write the test file |
--force |
flag | — | overwrite existing files |
--print |
flag | — | print instead of writing |
manage.py devx_openapi¶
Export a NinjaAPI's OpenAPI schema, or generate a typed client from it.
| Argument | Type | Default | Description |
|---|---|---|---|
api |
value | — | dotted path to a NinjaAPI, e.g. config.urls.api |
--format |
one of json, typescript, python |
'json' |
json (schema), typescript (types + fetch client) or python (httpx clients) |
--python-style |
one of pydantic, typeddict |
'pydantic' |
python models: pydantic (validated, with constraints) or typeddict |
--output |
value | — | file to write (default: print) |
--path-prefix |
value | — | override the API root path (default: from urls) |
--check |
flag | — | fail if --output is missing or outdated (for CI) |
--against |
value | — | compare with a previous OpenAPI document and fail on breaking changes; with --output the new document is written afterwards |
manage.py devx_inspect¶
Show the resolved policy of mounted controllers: scoping, permissions, transaction, pagination, relations and operations.
| Argument | Type | Default | Description |
|---|---|---|---|
target |
value | — | A controller path (app.api.PostController) or a mounted route prefix (/v1/posts). Without it, inspect every controller of the configured APIs. |
--json |
flag | — | emit JSON instead of a tree |
manage.py devx_doctor¶
Findings beyond manage.py check: unindexed query fields, unenforced owner/tenant fields, unhinted N+1 risks, missing pagination or permissions, and soft-deleted models with globally unique fields.
| Argument | Type | Default | Description |
|---|---|---|---|
target |
value | — | A controller path (app.api.PostController) or a mounted route prefix (/v1/posts). Without it, check every controller of the configured APIs. |
--json |
flag | — | emit JSON instead of a table |
--fail-on |
one of info, warn |
— | exit 1 if a finding at or above this severity is found |
manage.py devx_uploads¶
Clean expired pending uploads for a configured UploadController.
| Argument | Type | Default | Description |
|---|---|---|---|
controller |
value | — | import path to the configured UploadController |
--limit |
value | 100 |
maximum pending records per run |
manage.py devx_startapp¶
Create a Django app laid out for ninja-devx: a controller, schemas, a service and tests (Django's startapp with the ninja-devx template).
| Argument | Type | Default | Description |
|---|---|---|---|
name |
value | — | Name of the app. |
directory |
value | — | Optional destination directory, created if needed. |
--template |
value | the ninja-devx template | Another template directory or archive. |
Every other option of Django's startapp (--extension, --name, --exclude) is accepted.
manage.py devx_startproject¶
Create a runnable ninja-devx project: settings wired with request id, security header and hardening middleware, a health check, JSON logging, a database from DATABASE_URL, and a Postgres compose file (Django's startproject with a template).
| Argument | Type | Default | Description |
|---|---|---|---|
name |
value | — | Name of the project. |
directory |
value | — | Optional destination directory, created if needed. |
--template |
value | the ninja-devx template | Another template directory or archive. |
--no-docker |
flag | — | skip compose.yaml (no local Postgres) |
--app |
value | — | also scaffold a first app with devx_startapp |
Every other option of Django's startproject (--extension, --name, --exclude) is accepted.
manage.py devx_apikey¶
Create, revoke or rotate scoped API keys.
| Argument | Type | Default | Description |
|---|---|---|---|
action |
one of create, revoke, rotate |
— | what to do |
--user |
value | — | username of the key owner (create) |
--name |
value | — | label of the key (create) |
--scope |
value | [] |
granted scope, repeatable |
--days |
value | — | expire after this many days |
--rate |
value | '' |
rate limit of the key, e.g. "1000/hour" |
--prefix |
value | — | prefix of the key to revoke |
manage.py devx_webhooks¶
deliver: send due webhook deliveries (once, or continuously with --loop). generate-key: print a new WEBHOOK_SECRET_KEYS key. encrypt-secrets: encrypt stored secrets with the first key (also after rotation).
| Argument | Type | Default | Description |
|---|---|---|---|
action |
one of deliver, stats, prune, retry, generate-key, encrypt-secrets |
— | what to do |
--database |
value | — | outbox database alias |
--days |
value | 30 |
retain terminal events this many days |
--delivery-id |
value | — | one unclaimed delivery to replay |
--limit |
value | 100 |
deliveries per batch |
--timeout |
value | 10.0 |
seconds per request |
--loop |
flag | — | keep polling |
--interval |
value | 2.0 |
seconds between polls |
--allow-http |
flag | — | also send to http:// URLs (development) |
--allow-private-networks |
flag | — | also send to loopback and private addresses (development) |
manage.py devx_jobs¶
prune --older-than DAYS: delete terminal jobs older than this. retry
| Argument | Type | Default | Description |
|---|---|---|---|
action |
one of prune, retry |
— | what to do |
id |
value | — | job id (retry) |
--older-than |
value | — | prune threshold |
--retry-count |
value | — | extra attempts on failure (retry) |
python -m ninja_devx.tooling.unasync¶
| Argument | Type | Default | Description |
|---|---|---|---|
ASYNC.py:SYNC.py |
one or more pairs | — | source (async) and generated (sync) files |
--check |
flag | — | fail when a generated file is outdated |
--replace OLD=NEW |
repeatable | — | extra name mapping |
Default renames: AbstractAsyncContextManager → AbstractContextManager, AsyncExitStack → ExitStack, AsyncGenerator → Generator, AsyncIterable → Iterable, AsyncIterator → Iterator, AsyncOperationHook → OperationHook, AsyncRepository → Repository, AsyncResolver → Resolver, StopAsyncIteration → StopIteration, __aenter__ → __enter__, __aexit__ → __exit__, __aiter__ → __iter__, __anext__ → __next__, aadd → add, aaggregate → aggregate, aauthenticate → authenticate, aauthenticated_user → authenticated_user, abulk_create → bulk_create, abulk_update → bulk_update, achange → change, acheck_object_permissions → check_object_permissions, acheck_permissions → check_permissions, aclose → close, acontains → contains, acount → count, acreate → create, acurrent_user → current_user, adelete → delete, aearliest → earliest, aexists → exists, aexplain → explain, afirst → first, aget → get, aget_object → get_object, aget_object_or_404 → get_object_or_404, aget_or_create → get_or_create, ahas_object_permission → has_object_permission, ahas_perm → has_perm, ahas_permission → has_permission, ahas_perms → has_perms, ain_bulk → in_bulk, aiterator → iterator, alast → last, alatest → latest, anext → next, arefresh_from_db → refresh_from_db, aremove → remove, arequest_context → request_context, arequest_scope → request_scope, arequest_user → request_user, aresolve → resolve, asave → save, asynccontextmanager → contextmanager, aupdate → update, aupdate_or_create → update_or_create, enter_async_context → enter_context.
System checks¶
| Id | Level | Problem |
|---|---|---|
ninja_devx.E001 |
error | A CHECK_APIS entry cannot be imported or is not a NinjaAPI. |
ninja_devx.E002 |
error | search_fields, filter_fields, ordering_fields or default_ordering names a missing model field. |
ninja_devx.W003 |
warning | An output schema field is not a model field or attribute, and the schema does not resolve it. |
ninja_devx.E004 |
error | service_class needs constructor arguments, but the controller has no container. |
ninja_devx.W005 |
warning | An output field that VisibleTo can hide is required instead of optional. |
ninja_devx.W006 |
warning | A related hint or @requires_related lookup is not a relation of the model. |
ninja_devx.W007 |
warning | expand_rules[...].limit targets a relation that is not a plain reverse foreign key, so it is prefetched without a cap. |
ninja_devx.E007 |
error | aggregate_fields names a field that does not exist on the model. |