Validation¶
django-modern-rest has several layers of import-time and runtime validation.
We try to do everything that we can during import-time,
so it won’t affect your requests and responses.
However, we have to validate requests and responses during the runtime. Responses validation can be turned off in production for speed.
What do we validate and how?
Settings validation¶
We start with settings validation.
We only validate settings once per application,
we do it when the first Controller is created.
We also validate our own default values to be correct.
See SettingsValidator for the API.
Endpoint validation¶
Next, when controller is being created,
we run Endpoint validation.
Here we can detect all kinds of problems with how endpoints are defined:
Invalid
modify()orvalidate()usageOr invalid
HttpSpecusage
See EndpointMetadataBuilder
and EndpointMetadataBuilder
for the API.
HttpSpec validation¶
You can customize the strictness of HTTP Spec validation with overriding
disabled HttpSpec options per-endpoint,
per-controller, and globally.
Warning
We don’t recommend overriding any of these settings by default. It only makes sense to change, when implementing some old legacy API the “same” way as it used to be.
And only when you need this for a very specific reason.
1from http import HTTPStatus
2
3from dmr import Controller, modify
4from dmr.plugins.pydantic import PydanticSerializer
5
6
7class JobController(Controller[PydanticSerializer]):
8 @modify(status_code=HTTPStatus.NO_CONTENT)
9 def post(self) -> None:
10 print('Job created') # noqa: WPS421
11
Run result
$ curl http://127.0.0.1:8000/api/job/ -D - -X POST
HTTP/1.1 204 No Content
date: Sun, 26 Apr 2026 21:12:33 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 0
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
1from http import HTTPStatus
2
3from dmr import Controller, modify
4from dmr.plugins.pydantic import PydanticSerializer
5from dmr.settings import HttpSpec
6
7
8class JobController(Controller[PydanticSerializer]):
9 @modify(
10 status_code=HTTPStatus.NO_CONTENT,
11 no_validate_http_spec={HttpSpec.empty_response_body},
12 )
13 def post(self) -> int:
14 job_id = 4 # very random number :)
15 return job_id # noqa: RET504
16
Run result
$ curl http://127.0.0.1:8000/api/job/ -D - -X POST
HTTP/1.1 204 No Content
date: Sun, 26 Apr 2026 21:12:33 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 1
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
1from http import HTTPStatus
2
3from dmr import Controller, modify
4from dmr.plugins.pydantic import PydanticSerializer
5from dmr.settings import HttpSpec
6
7
8class JobController(Controller[PydanticSerializer]):
9 no_validate_http_spec = frozenset((HttpSpec.empty_response_body,))
10
11 @modify(status_code=HTTPStatus.NO_CONTENT)
12 def post(self) -> int:
13 job_id = 4 # very random number :)
14 return job_id # noqa: RET504
15
Run result
$ curl http://127.0.0.1:8000/api/job/ -D - -X POST
HTTP/1.1 204 No Content
date: Sun, 26 Apr 2026 21:12:33 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 1
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
Controller validation¶
The last step is the final Controller
validation which has everything ready:
Here we validate:
That all
Controllerclasses have unique methodsThat all endpoints are either sync or async
All per-controller and per-endpoint error handling
See ControllerValidator for the API.
Response validation¶
The last step is to validate the response when returning it from the endpoint in runtime. We need this to make sure that API responses always match response schemas.
It can be turned off.
See ResponseValidator for the API.
API Reference¶
Settings¶
Endpoint¶
- class dmr.validation.endpoint_metadata.EndpointMetadataBuilder(*, payload: ValidateEndpointPayload | ModifyEndpointPayload | None, controller_cls: type[Controller[BaseSerializer]], func: Callable[[...], Any], metadata_cls: type[EndpointMetadata], response_modification_cls: type[ResponseModification], component_parsers: list[tuple[ComponentParser, Any, tuple[Any, ...]]], type_annotations: dict[str, Any])[source]¶
Validate the metadata definition.
It is done during import-time only once, so it can be not blazing fast. It is better to be precise here than to be fast.
Here we only do structure and required validation. All semantic validation will be performed later on.
Metadata will NOT be considered ready after running this process.
- __call__() EndpointMetadata[source]¶
Do the validation.
- class dmr.validation.endpoint_metadata.EndpointMetadataValidator(*, metadata: EndpointMetadata)[source]¶
Builds responses for the endpoint metadata.
Runs semantic validation.
Metadata will be considered ready after running this process.
- __call__(func: Callable[[...], Any], payload: ValidateEndpointPayload | ModifyEndpointPayload | None, *, controller_cls: type[Controller[BaseSerializer]]) None[source]¶
Collect and validate all responses.
- response_list_validator_cls¶
alias of
_ResponseListValidator
Controller¶
- class dmr.validation.controller.ControllerValidator[source]¶
Validates that controller is created correctly.
- __call__(controller: type[Controller[BaseSerializer]]) bool | None[source]¶
Run the validation.
Response¶
- class dmr.validation.response.ResponseValidator(metadata: EndpointMetadata, serializer: type[BaseSerializer])[source]¶
Response validator.
Can validate responses that return raw data as well as real
HttpResponsethat are returned from endpoints.- validate_modification(endpoint: Endpoint, controller: Controller[BaseSerializer], structured: Any) ValidatedModification[source]¶
Validate structured data before dumping it to json.
- validate_response(endpoint: Endpoint, controller: Controller[BaseSerializer], response: _ResponseT) _ResponseT[source]¶
Validate response based on provided schema.