About dmr
dmr is a coding-agent skill for building and reviewing Django REST APIs with django-modern-rest. It guides typed controllers, routing, security, OpenAPI, and version-aware documentation use.
Key features
- 01Controller and request component guidance
- 02Sync and async endpoint checks
- 03Routing, auth, and throttling guidance
- 04OpenAPI generation from types
- 05Code review and testing checklist
Use cases
- 01Build Django REST APIs
- 02Review existing dmr code
- 03Verify endpoint and OpenAPI changes
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ wemake-services/ django-modern-rest --skill dmrAfter installing, type/dmrin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
.claude-plugin/plugin.jsonagents/openai.yamlreferences/controllers.mderrors.mdopenapi.mdrouting.mdsecurity.mdtesting.mdvalidation.mdDetails
From the project READMEdjango-modern-rest (dmr)
django-modern-rest is a typed, sync-and-async REST layer for Django:
Controller classes with one method per HTTP verb, DTOs parsed from
Body, Query, Headers, Cookies, and Path annotations,
Router based URLs, built-in auth and throttling,
and OpenAPI generated from the types.
When to use
- Any file that imports from
dmr, or a project withdjango-modern-restin its dependencies. - Requests for a "typed", "modern", or "async" Django REST API.
- Reviews of existing
dmrcode: use the checklist at the end.
For migrations from other frameworks use dmr-from-drf,
dmr-from-django-ninja, or dmr-from-dj-rest-auth.
For version bumps use dmr-upgrade.
For scaffolding from an OpenAPI document use dmr-openapi-skeleton.
Workflow
- Check the installed version and extras:
uv pip show django-modern-rest(or the lock file). The API changes between minor releases, so read the docs for the installed version, not the latest one. - Load the docs you need, they are versioned and LLM friendly:
- index:
https://django-modern-rest.readthedocs.io/en/<version>/llms.txt - one page as Markdown: replace
.htmlwith.mdin any docs URL, for examplehttps://django-modern-rest.readthedocs.io/en/latest/pages/routing.md - everything at once:
https://django-modern-rest.readthedocs.io/en/<version>/llms.txt
- index:
- Read the reference for the area you are changing (see the table below), it holds the full rules with correct and wrong examples.
- Verify: run
python manage.py check, the project's type checker, and tests written with thedmr_client/dmr_rffixtures. For OpenAPI changes, runpython manage.py dmr_export_schema <path>and review the diff.
Non-negotiable rules
Install django-modern-rest[msgspec] even for pydantic projects,
it makes JSON parsing fastest. Add django-stubs[compatible-mypy]
to dev dependencies, dmr relies on Django types.
Name component parameters exactly parsed_body, parsed_query,
parsed_headers, parsed_path, and parsed_cookies.
Other names are silently not parsed:
class UserController(Controller[MsgspecSerializer]):
def post(self, parsed_body: Body[UserModel]) -> UserModel:
return parsed_body
Optional components keep None inside the annotation:
parsed_body: Body[UserModel | None] = None.
Body[UserModel] | None = None is an import-time error.
Do not ever use return HttpResponse(...), because it bypasses negotiation,
headers, cookies, and validation.
Instead use self.to_response(...) or self.to_error(...) in controllers.
Raise APIError for errors,
handle them in handle_error / handle_async_error, not in the endpoint body:
raise APIError(
self.format_error('Not found', error_type=ErrorType.user_msg),
status_code=HTTPStatus.NOT_FOUND,
)
Prefer @modify-styled endpoints to @validated-styled endpoints.
Use plain methods by default. Add @modify(...) only for a status code,
headers, cookies, auth, throttling, or extra responses that differ
from the inferred ones. Use @validate(ResponseSpec(...)) only when
the endpoint returns an HttpResponse on purpose.
Prefer MsgspecSerializer. With pydantic and JSON only,
prefer PydanticFastSerializer over PydanticSerializer.
Prefer BodyMsgspec over Body when MsgspecSerializer is used
and all endpoint parsers decode into models,
like MsgspecJsonParser and MsgpackParser.
Use dmr.routing.path and Router, and install
build_404_handler / build_500_handler so API errors are JSON.
If CSRF and csrf_exempt = False are used always set
CSRF_FAILURE_VIEW = build_csrf_handler(router.prefix, ...)
error handler.
Keep validate_responses on in development and tests, turn it off only
in production settings. Never disable semantic_responses,
never set empty parsers or renderers.
Match sync and async: sync endpoints get sync error handlers and auth, async endpoints get async ones. Throttle login endpoints before auth.
Route auth endpoints from concrete_views with as_view(serializer=...),
do not subclass dmr.security.*.views controllers
only to restate the default payload and response.
Subclass views only when the payload, the response, or a hook differs:
path(
'auth/',
concrete_views.ObtainTokensSyncController.as_view(
serializer=MsgspecSerializer,
),
)
as_view silently ignores throttling= and auth=. Concrete views
use Settings.throttling, a login-specific throttle needs a views subclass.
Test with dmr_rf (DMRRequestFactory) for unit tests and dmr_client
(DMRClient) for full-stack tests, generate payloads with polyfactory,
and use schemathesis against the OpenAPI schema.
Quick reference
| Topic | Rules | Read |
|---|---|---|
Controllers, @modify vs @validate, serializers, responses, components, redirects | 9 | references/controllers.md |
| Routing, 404 / 500 handlers, sync and async app layout | 4 | references/routing.md |
Error handlers, APIError, custom error_model | 4 | references/errors.md |
Response validation, HttpSpec, settings | 5 | references/validation.md |
Ready-to-use auth concrete_views, typed authenticated requests, throttling, wrap_middleware | 4 | references/security.md |
pytest style, DMRClient, DMRRequestFactory, polyfactory, schemathesis | 5 | references/testing.md |
| Docstrings as OpenAPI descriptions | 1 | references/openapi.md |
Every rule links to the documentation page that explains it.
Review checklist
Flag these when reviewing dmr code:
- A component parameter with a name other than
parsed_*. Body[Model] | None = Noneinstead ofBody[Model | None] = None.HttpResponse(...)orJsonResponse(...)returned from an endpoint.try/exceptreturning error responses inside an endpoint body.@modifyor@validatethat changes nothing compared to the defaults.isinstance(exc, APIError)inside an error handler.validate_responses = Falseoutside production settings,semantic_responsesdisabled, or empty parsers / renderers.- A sync
handle_erroron an async controller, or the other way around. RemoteAddr(runs_before_auth=False)on a login endpoint.- A subclass of an auth controller from
dmr.security.*.viewsthat keeps the default payload and response, where itsconcrete_viewscounterpart does the same. - Cookie
concrete_viewswithoutjwt_refresh_cookie_path, so the refresh token is sent with every request. throttling=,auth=, orresponses=passed toas_view(), they are silently ignored there.RedirectTo(next_url)with a user-provided URL and nourl_has_allowed_host_and_schemecheck.django.urls.pathwheredmr.routing.pathis a drop-in replacement.django.test.TestCase,RequestFactory, orClientwhere thedmr_rf/dmr_clientfixtures exist.- Controllers and endpoints without docstrings when OpenAPI is served.