Public API

Controller

class dmr.controller.Controller(**kwargs)[source]

Bases: Generic[_SerializerT_co], View

Defines API views as controllers.

Controller is a django.views.generic.base.View subclass that should be used as a base for all REST endpoints.

endpoint_cls

Class to create endpoints with.

Type:

ClassVar[type[dmr.endpoint.Endpoint]]

serializer

Serializer that is passed via type parameters. The main goal of the serializer is to serialize object to json and deserialize them from json. You can’t change the serializer simply by modifying the attribute in the controller class. Because it is already passed to many other places. To customize it: create a new class, subclass BaseSerializer, and pass the new type as a type argument to the controller.

Type:

ClassVar[type[dmr.serializer.BaseSerializer]]

settings_validator_cls

Runs settings validation once the first controller is created.

Type:

ClassVar[type[dmr.validation.settings.SettingsValidator]]

no_validate_http_spec

Set of http spec validation checks that we disable for this class.

Type:

ClassVar[collections.abc.Set[dmr.settings.HttpSpec] | None]

validate_responses

Boolean whether or not validating responses. Works in runtime, can be disabled for better performance.

Type:

ClassVar[bool | None]

semantic_responses

Should semantic responses be collected from different providers for all endpoints in this class.

Type:

ClassVar[bool | None]

exclude_semantic_responses

Set of semantic responses that user wants to disable.

Type:

ClassVar[collections.abc.Set[http.HTTPStatus] | None]

validate_events

Should this endpoint validate events? If not set, defaults to the validate_responses value. This value only matters if the response will be a streaming response that supports event validation.

Type:

ClassVar[bool | None]

responses

List of responses schemas that this controller can return. Also customizable in endpoints and globally with 'responses' key in the settings.

Type:

ClassVar[collections.abc.Sequence[dmr.metadata.ResponseSpec]]

allowed_http_methods

Set of names to be treated as names for endpoints. Does not include options, but includes meta.

Type:

ClassVar[collections.abc.Set[str]]

parsers

Sequence of parsers to be used for this controller to parse incoming request’s body. All instances must be of subtypes of Parser.

Type:

ClassVar[collections.abc.Sequence[dmr.parsers.Parser]]

renderers

Sequence of renderers to be used for this controller to render response’s body. All instances must be of subtypes of Renderer.

Type:

ClassVar[collections.abc.Sequence[dmr.renderers.Renderer]]

auth

Sequence of auth instances to be used for this controller. Sync controllers must use instances of dmr.security.SyncAuth. Async controllers must use instances of dmr.security.AsyncAuth. Set it to None to disable auth of this controller.

Type:

ClassVar[collections.abc.Sequence[dmr.security.base.SyncAuth] | collections.abc.Sequence[dmr.security.base.AsyncAuth] | None]

error_model

Schema type that represents and validates common error responses.

Type:

ClassVar[Any]

is_abstract

Whether or not this controller is abstract. We consider controller “abstract” when it does not have exact serializer type.

Type:

ClassVar[bool]

streaming

Does this controller work with streaming responses like SSE?

Type:

ClassVar[bool]

controller_validator_cls

Runs full controller validation on definition.

Type:

ClassVar[type[dmr.validation.controller.ControllerValidator]]

annotations_context

Inference context to call typing.get_type_hints() for this controller.

Type:

ClassVar[dmr.types.AnnotationsContext]

api_endpoints

Dictionary of HTTPMethod name to controller instance.

Type:

ClassVar[collections.abc.Mapping[str, dmr.endpoint.Endpoint]]

csrf_exempt

Should this controller be exempted from the CSRF check? Is True by default.

Type:

ClassVar[bool]

summary

A short summary of what this path item does.

Type:

ClassVar[str | None]

description

A verbose explanation of the path item behavior.

Type:

ClassVar[str | None]

servers

An alternative servers array to service this path item.

Type:

ClassVar[collections.abc.Sequence[dmr.openapi.objects.server.Server] | None]

request

Current HttpRequest instance.

args

Path positional parameters of the request.

kwargs

Path named parameters of the request.

Type:

dict[str, Any]

classmethod __init_subclass__() None[source]

Construct a controller.

classmethod as_view(**initkwargs: Any) Callable[[...], HttpResponseBase][source]

Returns a view function for the class-based view.

This override applies CSRF exemption to the view. Session-based authentication will still be explicitly validated for CSRF, while all other authentication methods will be CSRF-exempt.

dispatch(request: HttpRequest, *args: Any, **kwargs: Any) HttpResponseBase[source]

Find an endpoint that serves this HTTP method and call it.

Return 405 if this method is not allowed.

format_error(error: str | Exception, *, loc: str | list[str | int] | None = None, error_type: str | ErrorType | None = None) Any[source]

Convert error to the common format.

Parameters:
  • error – A serialization exception like a validation error.

  • loc – Location where this error happened. Like "headers", or "field_name", or ["parsed_headers", "header_name"].

  • error_type – Optional type of the error for extra metadata.

Returns:

Simple python object - exception converted to a common format.

classmethod get_path_item(path: str, pattern: URLPattern, context: OpenAPIContext) PathItem[source]

Generate OpenAPI spec for path items.

async handle_async_error(endpoint: Endpoint, controller: Controller[_SerializerT_co], exc: Exception) HttpResponse[source]

Return error response if possible. Async case.

Override this method to add custom error handling for async execution. By default - does nothing, only re-raises the passed error. Won’t be called when using sync endpoints.

handle_error(endpoint: Endpoint, controller: Controller[_SerializerT_co], exc: Exception) HttpResponse[source]

Return error response if possible. Sync case.

Override this method to add custom error handling for sync execution. By default - does nothing, only re-raises the passed error. Won’t be called when using async endpoints.

handle_method_not_allowed(method: str) HttpResponse[source]

Return error response for 405 response code.

It is special in way that we don’t have an endpoint associated with it.

http_method_not_allowed(request: HttpRequest, *args: Any, **kwargs: Any) HttpResponse[source]

Do not use, use handle_method_not_allowed() instead.

View.http_method_not_allowed raises an error in a wrong format.

options(request: HttpRequest, *args: Any, **kwargs: Any) HttpResponse[source]

Do not use, define your own meta method instead.

Django’s View.options has incompatible signature with django-modern-rest. It would be a typing error to define something like:

Warning

Don’t do this!

>>> from http import HTTPStatus
>>> from dmr import Controller, validate
>>> from dmr.plugins.pydantic import (
...     PydanticSerializer,
... )
>>> class MyController(Controller[PydanticSerializer]):
...     @validate(
...         ResponseSpec(
...             None,
...             status_code=HTTPStatus.NO_CONTENT,
...         ),
...     )
...     def options(self) -> HttpResponse:  # <- typing problem
...         ...

That’s why instead of options you should define our own meta method:

>>> class MyController(Controller[PydanticSerializer]):
...     @validate(
...         ResponseSpec(
...             None,
...             status_code=HTTPStatus.NO_CONTENT,
...         ),
...     )
...     def meta(self) -> HttpResponse:
...         allow = ','.join(
...             method.upper()
...             for method in self.allowed_http_methods
...         )
...         return self.to_response(
...             None,
...             status_code=HTTPStatus.NO_CONTENT,
...             headers={'Allow': allow},
...         )

Note

By default meta method is not provided for you. If you want to support OPTIONS http method with the default implementation, use:

>>> from dmr.options_mixins import MetaMixin

>>> class ControllerWithMeta(
...     MetaMixin,
...     Controller[PydanticSerializer],
... ): ...
setup(request: HttpRequest, *args: Any, **kwargs: Any) None[source]

Set request context.

Unlike setup() does not set head method automatically.

Thread safety: there’s only one controller instance per request.

to_error(raw_data: Any, *, status_code: HTTPStatus, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, renderer: Renderer | None = None) HttpResponse[source]

Helpful method to convert API error parts into an actual error.

Always requires the error code to be passed.

Should be always used instead of using raw django.http.HttpResponse objects. Does the usual validation, no “second validation” problem exists.

to_response(raw_data: Any, *, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, status_code: HTTPStatus | None = None, renderer: Renderer | None = None) HttpResponse[source]

Helpful method to convert response parts into an actual response.

Should be always used instead of using raw django.http.HttpResponse objects. Has better serialization speed and semantics than manual. Does the usual validation, no “second validation” problem exists.

Endpoint

class dmr.endpoint.Endpoint(func: Callable[[...], Any], *, controller_cls: type[Controller[BaseSerializer]])[source]

Represents the single API endpoint.

Is built during the import time. In the runtime only does response validate, which can be disabled.

__call__(controller: Controller[BaseSerializer], *args: Any, **kwargs: Any) HttpResponseBase[source]

Run the endpoint and return the response.

get_operation_id(path: str, controller_name: str, serializer: type[BaseSerializer], context: OpenAPIContext) str[source]

Customize how OperationId is generated for the OpenAPI.

get_schema(path: str, pattern: URLPattern, controller_name: str, serializer: type[BaseSerializer], context: OpenAPIContext) Operation[source]

Build an OpenAPI Operation from an endpoint.

async handle_async_error(controller: Controller[BaseSerializer], exc: Exception) HttpResponse[source]

Return error response if possible.

Override this method to add custom async error handling.

handle_error(controller: Controller[BaseSerializer], exc: Exception) HttpResponseBase[source]

Return error response if possible.

Override this method to add custom error handling.

metadata_builder_cls

alias of EndpointMetadataBuilder

metadata_cls

alias of EndpointMetadata

metadata_validator_cls

alias of EndpointMetadataValidator

request_negotiator_cls

alias of RequestNegotiator

response_modification_cls

alias of ResponseModification

response_negotiator_cls

alias of ResponseNegotiator

response_validator_cls

alias of ResponseValidator

serializer_context_cls

alias of SerializerContext

class dmr.metadata.EndpointMetadata(*, endpoint_name: str, type_annotations: dict[str, Any], responses: dict[HTTPStatus, ResponseSpec], validate_responses: bool | None, method: str, modification: ResponseModification | None, error_handler: SyncErrorHandler | AsyncErrorHandler | None, component_parsers: list[tuple[ComponentParser, Any, tuple[Any, ...]]], parsers: dict[str, Parser], renderers: dict[str, Renderer], auth: list[SyncAuth | AsyncAuth] | None, no_validate_http_spec: frozenset[HttpSpec], allowed_http_methods: frozenset[str], semantic_responses: bool, exclude_semantic_responses: frozenset[HTTPStatus], validate_events: bool, summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None)[source]

Base class for common endpoint metadata.

endpoint_name

Text representation of an endpoint name for better error messages.

Type:

str

type_annotations

Unmodified unnotations of the endpoint function, returned by the resolution method.

Type:

dict[str, Any]

responses

Mapping of HTTP method to response description. All possible responses that this API can return. Used for OpenAPI spec generation and for response validation.

Type:

dict[http.HTTPStatus, dmr.metadata.ResponseSpec]

method

String name of an HTTP method for this endpoint.

Type:

str

validate_responses

Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

Type:

bool | None

modification

Default modifications that are applied to the returned data. Can be None, when @validate is used.

Type:

dmr.metadata.ResponseModification | None

error_handler

Callback function to be called when this endpoint faces an exception.

Type:

SyncErrorHandler | AsyncErrorHandler | None

component_parsers

List of component parser specifications from the controller. Each spec is a tuple of (ComponentParser class, type args).

Type:

list[tuple[ComponentParser, Any, tuple[Any, …]]]

parsers

List of instances to be used for this endpoint to parse incoming request’s body. All instances must be of subtypes of Parser.

Type:

dict[str, Parser]

renderers

List of instances to be used for this endpoint to render response’s body. All instances must be of subtypes of Renderer.

Type:

dict[str, Renderer]

auth

list of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. When set it to None it means that auth is disabled for this endpoint.

Type:

list[SyncAuth | AsyncAuth] | None

no_validate_http_spec

Set of checks that user wants to disable for validation in this endpoint.

Type:

frozenset[HttpSpec]

allowed_http_methods

Set of extra HTTP methods that are allowed for this endpoint.

Type:

frozenset[str]

semantic_responses

Should semantic responses from different providers be collected?

Type:

bool

exclude_semantic_responses

Set of semantic responses that user wants to disable.

Type:

frozenset[http.HTTPStatus]

validate_events

Should this endpoint validate events? If not set, defaults to the validate_responses value. This value only matters if the response will be a streaming response that supports event validation.

Type:

bool

summary

A short summary of what the operation does.

Type:

str | None

description

A verbose explanation of the operation behavior.

Type:

str | None

tags

A list of tags for API documentation control. Used to group operations in OpenAPI documentation.

Type:

list[str] | None

operation_id

Unique string used to identify the operation.

Type:

str | None

deprecated

Declares this operation to be deprecated.

Type:

bool

security

A declaration of which security mechanisms can be used for this operation. List of security requirement objects.

external_docs

Additional external documentation for this operation.

Type:

ExternalDocumentation | None

callbacks

A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses.

Type:

dict[str, Callback | Reference] | None

servers

An alternative servers array to service this operation. If a servers array is specified at the Path Item Object or OpenAPI Object level, it will be overridden by this value.

Type:

list[Server] | None

method can be a custom name, not specified in http.HTTPMethod enum, when allowed_http_methods is used for endpoint definition. This might be useful for cases like when you need to define a method like query, which is not yet formally accepted. Or provide domain specific HTTP methods.

collect_response_specs(controller_cls: type[Controller[BaseSerializer]], existing_responses: dict[HTTPStatus, ResponseSpec]) list[ResponseSpec][source]

Collect unique responses for all possible response providers.

response_spec_providers() list[type[ResponseSpecProvider]][source]

Determine: from where we should collect response schemas.

Override this method in your own metadata classes if you want more or less response spec providers.

For example: you can add some custom field to Controller like checks=. And you can subclass EndpointMetadata to also contain checks field and override this method to also include response specs from this field.

Define semantic_responses to False on settings or controller level to disable semantic responses collection.

@dmr.endpoint.modify(*, error_handler: AsyncErrorHandler, status_code: HTTPStatus | None = None, headers: Mapping[str, NewHeader | HeaderSpec] | None = None, cookies: Mapping[str, NewCookie | CookieSpec] | None = None, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = frozenset(), validate_events: bool | None = None, extra_responses: list[ResponseSpec] | None = None, no_validate_http_spec: Set[HttpSpec] | None = frozenset(), parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[AsyncAuth] | Sequence[SyncAuth] | None = (), summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None, response_description: str | None = None) _ModifyAsyncCallable[source]
@dmr.endpoint.modify(*, error_handler: SyncErrorHandler, status_code: HTTPStatus | None = None, headers: Mapping[str, NewHeader | HeaderSpec] | None = None, cookies: Mapping[str, NewCookie | CookieSpec] | None = None, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = frozenset(), validate_events: bool | None = None, extra_responses: list[ResponseSpec] | None = None, no_validate_http_spec: Set[HttpSpec] | None = frozenset(), parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[AsyncAuth] | Sequence[SyncAuth] | None = (), summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None, links: dict[str, Link | Reference] | None = None, response_description: str | None = None) _ModifySyncCallable
@dmr.endpoint.modify(*, status_code: HTTPStatus | None = None, headers: Mapping[str, NewHeader | HeaderSpec] | None = None, cookies: Mapping[str, NewCookie | CookieSpec] | None = None, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = frozenset(), validate_events: bool | None = None, extra_responses: list[ResponseSpec] | None = None, no_validate_http_spec: Set[HttpSpec] | None = frozenset(), error_handler: None = None, parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[AsyncAuth] | Sequence[SyncAuth] | None = (), summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None, links: dict[str, Link | Reference] | None = None, response_description: str | None = None) _ModifyAnyCallable

Decorator to modify endpoints that return raw model data.

Apply it to change some API parts:

>>> from http import HTTPStatus
>>> from dmr import Controller, modify
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class TaskController(Controller[PydanticSerializer]):
...     @modify(status_code=HTTPStatus.ACCEPTED)
...     def post(self) -> list[int]:
...         return [1, 2]  # id of tasks you have started
Parameters:
  • status_code – Shows status_code in the documentation. When status_code is passed, always use it by default. When not provided, we use smart inference based on the HTTP method name for default returned response.

  • headers – Shows headers in the documentation. When headers are passed we will add them for the default response.

  • cookies – Shows cookies in the documentation. When cookies are passed we will add them for the default response.

  • validate_responses – Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

  • semantic_responses – Should semantic responses be collected from different providers for this endpoint.

  • exclude_semantic_responses – Set of semantic responses status codes that user wants to disable.

  • validate_events – Should this endpoint validate events? If not set, defaults to the validate_responses value. This value only matters if the response will be a streaming response that supports event validation.

  • extra_responses – List of extra responses that this endpoint can return.

  • no_validate_http_spec – Set of http spec validation checks that we disable for this endpoint.

  • error_handler – Callback function to be called when this endpoint faces an exception.

  • parsers – Sequence of types to be used for this endpoint to parse incoming request’s body. All types must be subtypes of Parser.

  • renderers – Sequence of types to be used for this endpoint to render response’s body. All types must be subtypes of Renderer.

  • auth – Sequence of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. Set it to None to disable auth for this endpoint.

  • summary – A short summary of what the operation does.

  • description – A verbose explanation of the operation behavior.

  • tags – A list of tags for API documentation control. Used to group operations in OpenAPI documentation.

  • operation_id – Unique string used to identify the operation.

  • deprecated – Declares this operation to be deprecated.

  • external_docs – Additional external documentation for this operation.

  • callbacks – A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses.

  • servers – An alternative servers array to service this operation.

  • links – Possible links to other OpenAPI operations.

  • response_description – Description for the generated response object.

Returns:

The same function with __dmr_payload__ payload instance.

Warning

Do not disable validate_responses unless this is performance critical for you!

@dmr.endpoint.validate(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: AsyncErrorHandler, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = frozenset(), validate_events: bool | None = None, no_validate_http_spec: Set[HttpSpec] | None = frozenset(), parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[AsyncAuth] | Sequence[SyncAuth] | None = (), summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None) Callable[[Callable[[_ParamT], Awaitable[HttpResponseBase]]], Callable[[_ParamT], Awaitable[HttpResponseBase]]][source]
@dmr.endpoint.validate(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: SyncErrorHandler, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = frozenset(), validate_events: bool | None = None, no_validate_http_spec: Set[HttpSpec] | None = frozenset(), parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[AsyncAuth] | Sequence[SyncAuth] | None = (), summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None) Callable[[Callable[[_ParamT], HttpResponseBase]], Callable[[_ParamT], HttpResponseBase]]
@dmr.endpoint.validate(response: ResponseSpec, /, *responses: ResponseSpec, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = frozenset(), validate_events: bool | None = None, no_validate_http_spec: Set[HttpSpec] | None = frozenset(), error_handler: None = None, parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[AsyncAuth] | Sequence[SyncAuth] | None = (), summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None) Callable[[Callable[[_ParamT], _ResponseT]], Callable[[_ParamT], _ResponseT]]

Decorator to validate responses from endpoints that return HttpResponse.

Apply it to validate important API parts:

>>> from http import HTTPStatus
>>> from django.http import HttpResponse
>>> from dmr import Controller, validate, ResponseSpec
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class TaskController(Controller[PydanticSerializer]):
...     @validate(
...         ResponseSpec(
...             return_type=list[int],
...             status_code=HTTPStatus.OK,
...         ),
...     )
...     def post(self) -> HttpResponse:
...         return HttpResponse(b'[1, 2]', status=HTTPStatus.OK)

Response validation can be disabled for extra speed by sending validate_responses falsy parameter or by setting this configuration in your settings.py file:

settings.py
>>> DMR_SETTINGS = {'validate_responses': False}
Parameters:
  • response – The main response that this endpoint is allowed to return.

  • responses – A collection of other responses that are allowed to be returned from this endpoint.

  • validate_responses – Do we have to run runtime validation of responses for this endpoint? Customizable via global setting, per controller, and per endpoint. Here we only store the per endpoint information.

  • semantic_responses – Should semantic responses be collected from different providers for this endpoint.

  • exclude_semantic_responses – Set of semantic responses status codes that user wants to disable.

  • validate_events – Should this endpoint validate events? If not set, defaults to the validate_responses value. This value only matters if the response will be a streaming response that supports event validation.

  • no_validate_http_spec – Set of http spec validation checks that we disable for this endpoint.

  • error_handler – Callback function to be called when this endpoint faces an exception.

  • parsers – Sequence of types to be used for this endpoint to parse incoming request’s body. All types must be subtypes of Parser.

  • renderers – Sequence of types to be used for this endpoint to render response’s body. All types must be subtypes of Renderer.

  • auth – Sequence of auth instances to be used for this endpoint. Sync endpoints must use instances of dmr.security.SyncAuth. Async endpoints must use instances of dmr.security.AsyncAuth. Set it to None to disable auth for this endpoint.

  • summary – A short summary of what the operation does.

  • description – A verbose explanation of the operation behavior.

  • tags – A list of tags for API documentation control. Used to group operations in OpenAPI documentation.

  • operation_id – Unique string used to identify the operation.

  • deprecated – Declares this operation to be deprecated.

  • external_docs – Additional external documentation for this operation.

  • callbacks – A map of possible out-of band callbacks related to the parent operation. The key is a unique identifier for the Callback Object. Each value in the map is a Callback Object that describes a request that may be initiated by the API provider and the expected responses.

  • servers – An alternative servers array to service this operation.

Returns:

The same function with __dmr_payload__ payload instance.

Warning

Do not disable validate_responses unless this is performance critical for you!

Response, headers and cookies

class dmr.metadata.ResponseSpecProvider[source]

Base abstract class to provide extra response schemas.

abstractmethod classmethod provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) list[ResponseSpec][source]

Provide custom response specs.

Will be called to inject response specs from different components into the resulting endpoint metadata.

class dmr.metadata.ResponseSpec(return_type: Any, *, status_code: HTTPStatus, headers: Mapping[str, HeaderSpec] | None = None, cookies: Mapping[str, CookieSpec] | None = None, limit_to_content_types: Set[str] | None = None, streaming: bool = False, description: str | None = None, links: dict[str, Link | Reference] | None = None)[source]

Represents a single API response specification.

return_type

Shows return_type in the documentation as returned model schema. We validate return_type to match the returned response content by default, but it can be turned off.

Type:

Any

status_code

Shows status_code in the documentation. We validate status_code to match the specified one when HttpResponse is returned.

Type:

http.HTTPStatus

headers

Shows headers in the documentation. When passed, we validate that all given required headers are present in the final response.

Type:

collections.abc.Mapping[str, HeaderSpec] | None

cookies

Shows cookies in the documentation. When passed, we validate that all given required cookies are present in the final response.

Type:

collections.abc.Mapping[str, CookieSpec] | None

streaming

Are we working with the stream response?

Type:

bool

limit_to_content_types

This response can only happen only for given content types. By default, when equals to None, all responses can happen for all content types.

Type:

collections.abc.Set[str] | None

description

Text comment about what this response represents.

Type:

str | None

Possible links to other OpenAPI operations.

Type:

dict[str, Link | Reference] | None

We use this structure to validate responses and render them in OpenAPI.

get_schema(metadata: EndpointMetadata, serializer: type[BaseSerializer], context: OpenAPIContext) Response[source]

Returns the OpenAPI schema for the response.

Can be customized in subclasses. Be careful when overriding the schema generation. We don’t provide any validations for the returned schema. Ensure that it is in sync with the actual response.

class dmr.metadata.ResponseModification(*, return_type: Any, status_code: HTTPStatus, headers: Mapping[str, NewHeader | HeaderSpec] | None, cookies: Mapping[str, NewCookie | CookieSpec] | None, streaming: bool, description: str | None, links: dict[str, Link | Reference] | None)[source]

Represents a single API modification.

Parameters:
  • return_type – Shows return_type in the documentation as returned model schema. We validate return_type to match the returned response content by default, but it can be turned off.

  • status_code – Shows status_code in the documentation. We validate status_code to match the specified one when HttpResponse is returned.

  • headers – Shows headers in the documentation. Headers passed here will be added to the final response.

  • cookies – Shows cookies in the documentation. New cookies passed here will be added to the final response.

  • streaming – Are we working with the stream response?

  • description – Text comment about what this response represents.

  • links – Possible links to other OpenAPI operations.

We use this structure to modify the default response.

actionable_cookies() Mapping[str, NewCookie] | None[source]

Returns an optional mapping of cookies that should be added.

actionable_headers() Mapping[str, NewHeader] | None[source]

Returns an optional mapping of headers that should be added.

build_headers(renderer: Renderer) dict[str, str][source]

Returns headers with values for raw data endpoints.

infer_return_type() Any[source]

Infers return type if it needs some extra love.

response_spec_cls

alias of ResponseSpec

to_spec() ResponseSpec[source]

Convert response modification to response description.

exception dmr.response.APIError(raw_data: _ItemT, *, status_code: HTTPStatus, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None)[source]

Special class to fast return errors from API.

Does perform the regular response validation.

You can use APIError everywhere: - In endpoints - In components when parsing something - In auth if you want to change the response code

Usage:

>>> from http import HTTPStatus
>>> from dmr import (
...     APIError,
...     Controller,
...     ResponseSpec,
...     modify,
... )
>>> from dmr.errors import ErrorType
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class UserController(Controller[PydanticSerializer]):
...     @modify(
...         extra_responses=[
...             ResponseSpec(
...                 str,
...                 status_code=HTTPStatus.NOT_FOUND,
...             ),
...         ],
...     )
...     def get(self) -> str:
...         raise APIError(
...             self.format_error(
...                 'This API endpoint is not implemented yet',
...                 error_type=ErrorType.user_msg,
...             ),
...             status_code=HTTPStatus.NOT_FOUND,
...         )
dmr.response.build_response(serializer: type['BaseSerializer'], *, raw_data: Any, method: HTTPMethod | str, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, status_code: HTTPStatus | None = None, renderer: Renderer | None = None) HttpResponse[source]
dmr.response.build_response(serializer: type['BaseSerializer'], *, raw_data: Any, status_code: HTTPStatus, method: None = None, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | None = None, renderer: Renderer | None = None) HttpResponse

Utility that returns the actual HttpResponse object from its parts.

Does not perform extra validation, only regular response validation. We need this as a function, so it can be called when no endpoints exist.

Do not use directly, prefer using to_response() method. Unless you are using a lower-level API. Like in middlewares, for example.

You have to provide either method or status_code.

final class dmr.headers.HeaderSpec(*, description: str | None = None, deprecated: bool = False, example: str | None = None, required: bool = True, skip_validation: bool = False)[source]

Existing header that django.http.HttpResponse already has.

This class is used to describe the existing reality. Used for validation that all required headers are present.

description

Documentation, why this header is needed and what it does.

Type:

str | None

deprecated

Whether this header is deprecated.

Type:

bool

example

Documentation, what can be given as values in this header.

Type:

str | None

required

Whether or not this header can be missing.

Type:

bool

skip_validation

Is true, when header is only used for schema purposes, without any runtime validation. This might be useful, when this header will be set after our framework’s validation. For example, by django.contrib.sessions.middleware.SessionMiddleware or by HTTP proxy. This header might be present in runtime or might be missing.

Type:

bool

to_spec() HeaderSpec[source]

Needed for API compat with NewHeader.

final class dmr.headers.NewHeader(*, description: str | None = None, deprecated: bool = False, example: str | None = None, value: str)[source]

New header that will be added to django.http.HttpResponse by us.

This class is used to add new entries to response’s headers. Is not used for validation.

description

Documentation, why this header is needed and what it does.

Type:

str | None

deprecated

Whether this header is deprecated.

Type:

bool

example

Documentation, what can be given as values in this header.

Type:

str | None

value

value to be set in this new header.

Type:

str

to_spec() HeaderSpec[source]

Convert header type.

final class dmr.cookies.CookieSpec(*, path: str = '/', max_age: int | None = None, expires: int | None = None, domain: str | None = None, secure: bool | None = None, httponly: bool | None = None, samesite: Literal['lax', 'strict', 'none'] = 'lax', description: str | None = None, required: bool = True, skip_validation: bool = False)[source]

Description of a single cookie in Set-Cookie header.

path

Path fragment that must exist in the request url for the cookie to be valid. Defaults to /.

Type:

str

max_age

Maximal age of the cookie before its invalidated.

Type:

int | None

expires

Seconds from now until the cookie expires.

Type:

int | None

domain

Domain for which the cookie is valid.

Type:

str | None

secure

Https is required for the cookie.

Type:

bool | None

httponly

Forbids javascript to access the cookie via document.cookie.

Type:

bool | None

samesite

Controls whether or not a cookie is sent with cross-site requests. Defaults to 'lax'.

Type:

Literal[‘lax’, ‘strict’, ‘none’]

description

Description of the response cookie header for OpenAPI documentation.

Type:

str | None

required

Defines that this cookie can be missing in some cases.

Type:

bool

skip_validation

Is true, when cookie is only used for schema purposes, without any runtime validation. This might be useful, when this cookie will be set after our framework’s validation. For example, by django.contrib.sessions.middleware.SessionMiddleware or by HTTP proxy. This cookie might be present in runtime or might be missing.

Type:

bool

is_equal(other: Morsel[str]) bool[source]

Compare this object with SimpleCookie like object.

to_spec() CookieSpec[source]

API for compatibility with NewCookie.

final class dmr.cookies.NewCookie(*, path: str = '/', max_age: int | None = None, expires: int | None = None, domain: str | None = None, secure: bool | None = None, httponly: bool | None = None, samesite: Literal['lax', 'strict', 'none'] = 'lax', value: str)[source]

New cookie to be set for the response.

value

Value for the cookie.

Type:

str

path

Path fragment that must exist in the request url for the cookie to be valid. Defaults to /.

Type:

str

max_age

Maximal age of the cookie before its invalidated.

Type:

int | None

expires

Seconds from now until the cookie expires.

Type:

int | None

domain

Domain for which the cookie is valid.

Type:

str | None

secure

Https is required for the cookie.

Type:

bool | None

httponly

Forbids javascript to access the cookie via document.cookie.

Type:

bool | None

samesite

Controls whether or not a cookie is sent with cross-site requests. Defaults to 'lax'.

Type:

Literal[‘lax’, ‘strict’, ‘none’]

as_dict() dict[str, Any][source]

Converts to a dictionary .

to_spec() CookieSpec[source]

Converts the modification to spec.

dmr.cookies.set_cookies(response: HttpResponseBase, cookies: Mapping[str, NewCookie] | None) None[source]

Set cookies for the HTTP response.

Validation

class dmr.validation.ModifyEndpointPayload(*, summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, security: list[SecurityRequirement] | None = None, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = None, validate_events: bool | None = None, error_handler: Callable[[Endpoint, Controller[BaseSerializer], Exception], HttpResponse] | Callable[[Endpoint, Controller[BaseSerializer], Exception], Awaitable[HttpResponse]] | None = None, no_validate_http_spec: Set[HttpSpec] | None = None, parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[SyncAuth] | Sequence[AsyncAuth] | None = (), responses: list[ResponseSpec] | None, status_code: HTTPStatus | None, headers: Mapping[str, NewHeader | HeaderSpec] | None, cookies: Mapping[str, NewCookie | CookieSpec] | None, response_description: str | None, links: dict[str, Link | Reference] | None)[source]

Payload created by @modify.

class dmr.validation.ValidateEndpointPayload(*, summary: str | None = None, description: str | None = None, tags: list[str] | None = None, operation_id: str | None = None, deprecated: bool = False, security: list[SecurityRequirement] | None = None, external_docs: ExternalDocumentation | None = None, callbacks: dict[str, Callback | Reference] | None = None, servers: list[Server] | None = None, validate_responses: bool | None = None, semantic_responses: bool | None = None, exclude_semantic_responses: Set[HTTPStatus] | None = None, validate_events: bool | None = None, error_handler: Callable[[Endpoint, Controller[BaseSerializer], Exception], HttpResponse] | Callable[[Endpoint, Controller[BaseSerializer], Exception], Awaitable[HttpResponse]] | None = None, no_validate_http_spec: Set[HttpSpec] | None = None, parsers: Sequence[Parser] | None = None, renderers: Sequence[Renderer] | None = None, auth: Sequence[SyncAuth] | Sequence[AsyncAuth] | None = (), responses: list[ResponseSpec])[source]

Payload created by @validate.

Serialization

class dmr.serializer.BaseSerializer[source]

Abstract base class for data serialization.

What serializer does?

  1. It provides serialization and deserialization hooks for parser and renderer. So different parsers and renderers will work similarly. This way you can modify all the serialization logic in one place and not adjust all possible parsers or renderers

  2. It provides validation for raw python data, see dmr.serializer.BaseSerializer.from_python() method

  3. It provides serialization for related complex utility objects like validation errors and responses that don’t have .content attribute. For example: file and sse responses

validation_error

Exception type that is used for validation errors. Required to be set in subclasses.

Type:

ClassVar[type[Exception]]

optimizer

Endpoint optimizer. Type that pre-compiles / creates / caches models in import time. Required to be set in subclasses.

Type:

ClassVar[type[dmr.serializer.BaseEndpointOptimizer]]

openapi
abstractmethod classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) Any[source]

Convert json bytestring to structured data.

classmethod deserialize_hook(target_type: type[Any], to_deserialize: Any) Any[source]

Customize how some objects are deserialized from json.

Only add types that are common for all potential plugins here. Should be called inside deserialize().

abstractmethod classmethod from_python(unstructured: Any, model: Any, *, strict: bool | None) Any[source]

Parse unstructured data from python primitives into model.

Raises cls.validation_error when something cannot be parsed.

Parameters:
  • unstructured – Python objects to be parsed / validated.

  • model – Python type to serve as a model. Can be any type hints that user can theoretically supply. Depends on the serialization plugin.

  • strict – Whether we use more strict validation rules. For example, it is fine for a request validation to be less strict in some cases and allow type coercition. But, response types need to be strongly validated.

Returns:

Structured and validated data.

classmethod is_supported(pluggable: Parser | Renderer) bool[source]

Is this parser or renderer supported?

When defining custom serializers you can specify what kind of parser and renders you support. Adding a combination of unsupported serializer and parser / render will raise an import-time validation error.

abstractmethod classmethod serialize(structure: Any, *, renderer: Renderer) bytes[source]

Convert structured data to json bytestring.

classmethod serialize_hook(to_serialize: Any) Any[source]

Customize how some objects are serialized into json.

Only add types that are common for all potential plugins here. Should be called inside serialize().

abstractmethod classmethod serialize_validation_error(exc: Exception) list[ErrorDetail][source]

Convert specific serializer’s validation errors into simple python data.

Parameters:

exc – A serialization exception to be serialized into simpler type. For example, pydantic has a complex pydantic_core.ValidationError type. That can’t be converted to a simpler error message easily.

Returns:

Simple python object - exception converted to json.

abstractmethod classmethod to_python(structured: Any) Any[source]

Unstructure structured data from a model into Python primitives.

Parameters:

structured – Model instance.

Returns:

Unstructured data.

class dmr.serializer.BaseEndpointOptimizer[source]

Plugins might often need to run some specific preparations for endpoints.

To achieve that we provide an explicit API for that.

abstractmethod classmethod optimize_endpoint(metadata: EndpointMetadata) None[source]

Optimize the endpoint.

Parameters:

metadata – Endpoint metadata to optimize.

class dmr.endpoint.SerializerContext(func: Callable[[...], Any], controller_cls: type[Controller[BaseSerializer]], type_annotations: dict[str, Any])[source]

Parse and bind request components for a controller.

This context collects raw data for all registered components, validates the combined payload in a single call using a cached TypedDict model, and then binds the parsed values back to the controller.

strict_validation

Whether or not to validate payloads in strict mode. Strict mode in some serializers does not allow implicit type conversions. Defaults to None, which means that we decide on a per-field basis if it is set, if not then on a per-model basis.

Type:

ClassVar[bool | None]

__call__(endpoint: Endpoint, controller: Controller[BaseSerializer]) dict[str, Any][source]

Collect, validate, and bind component data to the controller.

Raises serializer.validation_error when provided data does not match the expected model.

component_builder_cls

alias of ComponentParserBuilder

class dmr.serializer.BaseSchemaGenerator[source]

Generates JSON schema by the native serializer API.

abstractmethod classmethod get_schema(model: Any, ref_template: str, *, used_for_response: bool = False) tuple[dict[str, Any], dict[str, Any]] | None[source]

Provide JSON schema / OpenAPI spec for the given model.

Parameters:
  • model – Model to generate JSON schema for.

  • ref_template – Reference template to use for the references.

  • used_for_response – Is this schema used for the response or request.

abstractmethod classmethod schema_name(model: Any) str | None[source]

Return a schema name for a model, if it exists.

It is done directly by the serializer, we don’t store any specific logic for it.

class dmr.components.ComponentParserBuilder(func: Callable[[...], Any], controller_cls: type[Controller[BaseSerializer]])[source]

Find the component parser types in the MRO and find model types for them.

Validates that component parsers can’t have type vars as models at this point.

__call__(type_annotations: dict[str, Any]) list[tuple[ComponentParser, Any, tuple[Any, ...]]][source]

Run the building process, infer type vars if needed.

type_var_inference_cls

alias of TypeVarInference

Routing

class dmr.routing.Router(prefix: str, urls: Sequence[URLPattern | URLResolver])[source]

Collection of HTTP routes for REST framework.

get_schema(context: OpenAPIContext) OpenAPI[source]

Builds OpenAPI specification.

This class orchestrates the process of generating a complete OpenAPI specification by collecting controllers from the router, generating path items for each controller, extracting shared components, and merging everything together with the configuration.

dmr.routing.build_404_handler(prefix: str, /, *prefixes: str, serializer: type[BaseSerializer], format_error: FormatError = <function format_error>, renderers: ~collections.abc.Sequence[Renderer] | None = None) Callable[[HttpRequest, Exception], HttpResponse][source]

Create a 404 handler that returns a response with content negotiation.

All prefixes are normalized to start with a leading slash. If the request path matches any of them, a 404 response is returned using the same serializer and renderers as your API. If the client’s Accept does not match any renderer, the first configured renderer is used. For non-matching paths, Django’s default page_not_found handler is used.

Parameters:
  • prefix – Path prefix (e.g. 'api/') for which to return API 404.

  • *prefixes – Additional path prefixes.

  • format_error – Callable used to build the error body for the response.

  • serializer – Serializer class used to serialize the error body.

  • renderers – Optional sequence of renderers. If omitted, uses renderers from settings.

dmr.routing.build_500_handler(prefix: str, /, *prefixes: str, serializer: type[BaseSerializer], format_error: FormatError = <function format_error>, renderers: ~collections.abc.Sequence[Renderer] | None = None) Callable[[HttpRequest], HttpResponse][source]

Create a 500 handler that returns a response with content negotiation.

All prefixes are normalized to start with a leading slash. If the request path matches any of them, a 500 response is returned using the same serializer and renderers as your API. If the client’s Accept does not match any renderer, the first configured renderer is used. For non-matching paths, Django’s default server_error handler is used.

Parameters:
  • prefix – Path prefix (e.g. 'api/') for which to return API 500.

  • *prefixes – Additional path prefixes.

  • format_error – Callable used to build the error body for the response.

  • serializer – Serializer class used to serialize the error body.

  • renderers – Optional sequence of renderers. If omitted, uses renderers from settings.

dmr.routing.path(route: _StrOrPromise, view: Callable[[...], HttpResponseBase], kwargs: dict[str, Any] | None = None, name: str | None = None) URLPattern[source]
dmr.routing.path(route: _StrOrPromise, view: Callable[[...], Coroutine[Any, Any, HttpResponseBase]], kwargs: dict[str, Any] | None = None, name: str | None = None) URLPattern
dmr.routing.path(route: _StrOrPromise, view: tuple[Sequence[URLPattern | URLResolver], str | None, str | None], kwargs: dict[str, Any] | None = None, name: str | None = None) URLResolver
dmr.routing.path(route: _StrOrPromise, view: Sequence[URLResolver | str], kwargs: dict[str, Any] | None = None, name: str | None = None) URLResolver

Creates URL pattern using prefix-based matching for faster routing.

Meta mixins

class dmr.options_mixins.MetaMixin[source]

Mixin that provides default meta method or OPTIONS http method.

Use it for sync controllers.

It just returns the list of allowed methods. Use it as a mixin with the dmr.controller.Controller type:

>>> from dmr import Controller
>>> from dmr.options_mixins import MetaMixin
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class SupportsOptionsHttpMethod(
...     MetaMixin,
...     Controller[PydanticSerializer],
... ): ...
meta() HttpResponse[source]

Default sync implementation for OPTIONS http method.

class dmr.options_mixins.AsyncMetaMixin[source]

Mixin that provides default meta method or OPTIONS http method.

Use it for async controllers.

It just returns the list of allowed methods. Use it as a mixin with the dmr.controller.Controller type:

>>> from dmr import Controller
>>> from dmr.options_mixins import AsyncMetaMixin
>>> from dmr.plugins.pydantic import PydanticSerializer

>>> class SupportsOptionsHttpMethod(
...     AsyncMetaMixin,
...     Controller[PydanticSerializer],
... ): ...
async meta() HttpResponse[source]

Default async implementation for OPTIONS http method.

Exceptions

final exception dmr.exceptions.UnsolvableAnnotationsError[source]

Raised when we can’t solve function’s annotations using get_type_hints.

Only raised when there are no other options.

final exception dmr.exceptions.EndpointMetadataError[source]

Raised when user didn’t specify some required endpoint metadata.

final exception dmr.exceptions.DataParsingError[source]

Raised when input data cannot be parsed.

final exception dmr.exceptions.RequestSerializationError[source]

Raised when we fail to parse some request part.

final exception dmr.exceptions.ResponseSchemaError[source]

Raised when we fail to validate some response part.

Can only happen when response validation is enabled. Does not show up in the response schema if validation is disabled.

final exception dmr.exceptions.ValidationError(payload: list[ErrorDetail], *, status_code: HTTPStatus = HTTPStatus.UNPROCESSABLE_ENTITY)[source]

Raised when we cannot properly validate request or response models.

It should be only raised when serializer raise its internal validation error.

It is an universal way of handling validation errors from different serializers.

final exception dmr.exceptions.NotAcceptableError[source]

Raised when client provides wrong Accept header.

final exception dmr.exceptions.NotAuthenticatedError(msg: str | Promise | None = None)[source]

Raised when we fail to authenticate a user.

final exception dmr.exceptions.InternalServerError[source]

Indicates that something is broken on our side.

If settings.DEBUG is enabled, we share the details: what has happened. If it disabled, we hust show a generic message.

Utilities

dmr.types.Json: TypeAlias = typing.Any

Recursive type alias for JSON data.

What is JSON? Integers, floats, booleans, strings, list of them and dicts of them, which keys are always strings.

In runtime it is always typing.Any because of the parsing complexity, while in type checking it correctly defined.

We don’t recommend using it for anything serious, it is better to define real models instead.

final class dmr.types.Empty[source]

Special value for empty defaults.

dmr.types.EmptyObj: Final = Empty()

Default singleton for empty values.

class dmr.types.AnnotationsContext(*, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None, include_extras: bool = True, format: Format | None = None)[source]

Annotation evaluation context.

Use this type to change how controllers resolve type hints of their endpoints.

For example, one can change this function to use inspect.get_annotations() function. Or to have some pre-defined global names.

__call__(endpoint_func: Callable[[...], Any]) dict[str, Any][source]

Get the annotations.

Parameters:

endpoint_func – function with return type annotation.

Returns:

Function’s parsed and solved return type.

Raises:

UnsolvableAnnotationsError – when annotation can’t be solved or when the annotation does not exist.

class dmr.types.TypeVarInference(to_infer: TypeVar, context: type[Any])[source]

Inferences type variables to the applied real type values.

__call__() dict[TypeVar, Any][source]

Run the inference.

Returns:

Mapping of type vars to its inferenced values. It can still be a type variable, if no real values are provided.

Decorators

dmr.decorators.dispatch_decorator(func: Callable[[...], Any]) Callable[[_TypeT], _TypeT][source]

Special helper to decorate class-based view’s dispatch method.

Use it directly on controllers, like so:

>>> from dmr import Controller
>>> from dmr.decorators import dispatch_decorator
>>> from dmr.plugins.pydantic import PydanticSerializer
>>> from django.contrib.auth.decorators import login_required

>>> @dispatch_decorator(login_required())
... class MyController(Controller[PydanticSerializer]):
...     def get(self) -> str:
...         return 'Logged in!'

In this example we would require all calls to all methods of MyController to require an existing authentication.

It also works for things like: - django.contrib.auth.decorators.login_not_required() - django.contrib.auth.decorators.user_passes_test() - django.contrib.auth.decorators.permission_required() - and any other default or custom django decorator

Danger

This will return non-json responses, without respecting your spec! Use with caution!

If you want full spec support, use middleware wrappers. You would probably want to use wrap_middleware() as well. Or use endpoint_decorator().

dmr.decorators.endpoint_decorator(original_decorator: Callable[[_ViewT], _ViewT]) Callable[[Callable[[_ParamT], _ReturnT]], Callable[[_ParamT], _ReturnT]][source]

Apply regular Django-styled decorator to a single endpoint.

Example:

>>> from http import HTTPStatus

>>> from dmr import Controller, HeaderSpec, modify
>>> from dmr.decorators import endpoint_decorator
>>> from dmr.plugins.pydantic import PydanticSerializer
>>> from django.contrib.auth.decorators import login_required

>>> class MyController(Controller[PydanticSerializer]):
...     @endpoint_decorator(login_required())
...     @modify(
...         extra_responses=[
...             ResponseSpec(
...                 None,
...                 status_code=HTTPStatus.FOUND,
...                 headers={'Location': HeaderSpec()},
...             ),
...         ],
...     )
...     def get(self) -> str:
...         return 'Logged in!'

It also works for things like: - django.contrib.auth.decorators.login_not_required() - django.contrib.auth.decorators.user_passes_test() - django.contrib.auth.decorators.permission_required() - and any other default or custom django decorator

Warning

Be careful with decorators that you apply. They will not escape the response validation, but will return unmodified responses from the original decorators.

For example: login_required will return a redirect. You can describe it with the extra metadata.

dmr.decorators.wrap_middleware(middleware: Callable[[Callable[[...], Any]], Callable[[...], Any]], response: ResponseSpec, *responses: ResponseSpec) Callable[[Callable[[HttpResponse], HttpResponse]], DecoratorWithResponses][source]

Factory function that creates a decorator with pre-configured middleware.

This allows creating reusable decorators with specific middleware and response handling.

Parameters:
  • middleware – Django middleware to apply

  • response – ResponseSpec for the middleware response

  • responses – Others ResponseSpec

Returns:

A function that takes a converter and returns a class decorator

>>> from django.views.decorators.csrf import csrf_protect
>>> from django.http import HttpResponse
>>> from http import HTTPStatus
>>> from dmr import Controller, ResponseSpec
>>> from dmr.response import build_response
>>> from dmr.plugins.pydantic import PydanticSerializer
>>> from dmr.errors import ErrorType, ErrorModel, format_error

>>> @wrap_middleware(
...     csrf_protect,
...     ResponseSpec(
...         return_type=ErrorModel,
...         status_code=HTTPStatus.FORBIDDEN,
...     ),
... )
... def csrf_protect_json(response: HttpResponse) -> HttpResponse:
...     return build_response(
...         PydanticSerializer,
...         raw_data=format_error(
...             'CSRF verification failed. Request aborted.',
...             error_type=ErrorType.user_msg,
...         ),
...         status_code=HTTPStatus(response.status_code),
...     )

>>> @csrf_protect_json
... class MyController(Controller[PydanticSerializer]):
...     responses = [
...         *csrf_protect_json.responses,
...     ]
...
...     def post(self) -> dict[str, str]:
...         return {'message': 'ok'}

Testing

class dmr.test.DMRRequestFactory(*, json_encoder=<class 'django.core.serializers.json.DjangoJSONEncoder'>, headers=None, query_params=None, **defaults)[source]

Test utility for testing apps using django-modern-rest.

Based on django.test.RequestFactory. See their docs for advanced usage: https://docs.djangoproject.com/en/dev/topics/testing/tools

This type, in contrast to a regular RequestFactory, sets content-type as application/json.

Sets WSGI environment.

class dmr.test.DMRAsyncRequestFactory(*, json_encoder=<class 'django.core.serializers.json.DjangoJSONEncoder'>, headers=None, query_params=None, **defaults)[source]

Version of DMRRequestFactory but for ASGI environment.

Uses the exactly the same API.

wrap(thing)[source]

Utility method for testing.

Pretends to wrap async controllers into async functions for typing. But in reality does nothing.

This happens due to the fact that View is typed as sync object.

class dmr.test.DMRClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, query_params=None, **defaults)[source]

Test utility for testing apps using django-modern-rest.

Based on django.test.Client. See their docs for advanced usage: https://docs.djangoproject.com/en/dev/topics/testing/tools/

This type, in contrast to a regular Client, sets content-type as application/json.

class dmr.test.DMRAsyncClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, query_params=None, **defaults)[source]

Async version of DMRClient.

Uses async API. Requires you to await calls to .get, .post, etc.

Plugins

Pydantic

class dmr.plugins.pydantic.PydanticSerializer[source]

Serialize and deserialize objects using pydantic.

Pydantic support is optional. To install it run:

pip install 'django-modern-rest[pydantic]'
to_json_kwargs

Dictionary of kwargs that will be passed to model serialization callbacks.

Type:

ClassVar[dmr.plugins.pydantic.serializer.ToJsonKwargs]

to_model_kwargs

Dictionary of kwargs that will be passed to model deserialization callbacks.

Type:

ClassVar[dmr.plugins.pydantic.serializer.ToModelKwargs]

classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) Any[source]

Convert string or bytestring to simple python object.

classmethod from_python(unstructured: Any, model: Any, *, strict: bool | None, rebuild_namespace: Mapping[str, Any] | None = None) Any[source]

Parse unstructured data from python primitives into model.

Parameters:
  • unstructured – Python objects to be parsed / validated.

  • model – Python type to serve as a model. Can be any type that pydantic supports. Examples: dict[str, int] and BaseModel subtypes.

  • strict – Whether we use more strict validation rules. For example, it is fine for a request validation to be less strict in some cases and allow type coercition. But, response types need to be strongly validated.

  • rebuild_namespace – Optional namespace to rebuild the type adapter. Should be used when there are forward references that pydantic cannot solve by itself.

Returns:

Structured and validated data.

Raises:

pydantic_core.ValidationError – When parsing can’t be done.

optimizer

alias of PydanticEndpointOptimizer

schema_generator

alias of PydanticSchemaGenerator

classmethod serialize(structure: Any, *, renderer: Renderer) bytes[source]

Convert any object to raw bytestring.

classmethod serialize_hook(to_serialize: Any) Any[source]

Customize how some objects are serialized into simple objects.

classmethod serialize_validation_error(exc: Exception) list[ErrorDetail][source]

Serialize validation error.

classmethod to_python(structured: Any) Any[source]

Unparse structured data from a model into Python primitives.

Parameters:

structured – Model instance.

Returns:

Unstructured data.

class dmr.plugins.pydantic.PydanticFastSerializer[source]

Fast pydantic serializer for cases when you only work with json.

Does not use parser and renderer passed objects, does not use dmr.plugins.pyndatic.PydanticSerializer.serialize_hook and dmr.plugins.pyndatic.PydanticSerializer.deserialize_hook method.

Is built for optimizations only, use with caution.

Only works with application/json content type.

Added in version 0.6.0: See issue 830.

classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) Any[source]

Fast way to serializer pyndatic models into json bytestring.

parser parameter is always ignored.

classmethod is_supported(pluggable: Parser | Renderer) bool[source]

Is this parser or renderer supported?

We only support json parsers and renderers.

classmethod serialize(structure: Any, *, renderer: Renderer) bytes[source]

Fast way to serializer pyndatic models into json bytestring.

renderer parameter is always ignored.

class dmr.plugins.pydantic.serializer.PydanticEndpointOptimizer[source]

Optimize endpoints that are parsed with pydantic.

classmethod optimize_endpoint(metadata: EndpointMetadata) None[source]

Create models for return types for validation.

class dmr.plugins.pydantic.schema.PydanticSchemaGenerator[source]

Generates JSON schema for pydantic objects.

classmethod get_schema(model: Any, ref_template: str, *, used_for_response: bool = False) tuple[dict[str, Any], dict[str, Any]] | None[source]

Proxies the JSON schema generation to pydantic itself.

classmethod schema_name(model: Any) str | None[source]

Return a schema name for a model, if it exists.

final class dmr.plugins.pydantic.serializer.ToJsonKwargs[source]

Keyword arguments for pydantic’s model dump method.

final class dmr.plugins.pydantic.serializer.ToModelKwargs[source]

Keyword arguments for pydantic’s python object validation method.

Msgspec

class dmr.plugins.msgspec.MsgspecSerializer[source]

Serialize and deserialize objects using msgspec.

Msgspec support is optional. To install it run:

pip install 'django-modern-rest[msgspec]'
to_json_kwargs

Dictionary of kwargs that will be passed to model serialization callbacks.

Type:

ClassVar[dmr.plugins.msgspec.serializer.ToJsonKwargs]

to_model_kwargs

Dictionary of kwargs that will be passed to model deserialization callbacks.

Type:

ClassVar[dmr.plugins.msgspec.serializer.ToModelKwargs]

classmethod deserialize(buffer: bytes | bytearray, *, parser: Parser, request: HttpRequest, model: Any) Any[source]

Convert string or bytestring to simple python object.

classmethod from_python(unstructured: Any, model: Any, *, strict: bool | None) Any[source]

Parse unstructured data from python primitives into model.

Parameters:
  • unstructured – Python objects to be parsed / validated.

  • model – Python type to serve as a model. Can be any type that msgspec supports. Examples: dict[str, int] and BaseModel subtypes.

  • strict – Whether we use more strict validation rules. For example, it is fine for a request validation to be less strict in some cases and allow type coercition. But, response types need to be strongly validated.

Returns:

Structured and validated data.

Raises:

msgspec.ValidationError – When parsing can’t be done.

optimizer

alias of MsgspecEndpointOptimizer

schema_generator

alias of MsgspecSchemaGenerator

classmethod serialize(structure: Any, *, renderer: Renderer) bytes[source]

Convert any object to a raw bytestring.

classmethod serialize_validation_error(exc: Exception) list[ErrorDetail][source]

Serialize validation error.

classmethod to_python(structured: Any) Any[source]

Unparse structured data from a model into Python primitives.

Parameters:

structured – Model instance.

Returns:

Unstructured data.

validation_error

alias of ValidationError

class dmr.plugins.msgspec.serializer.MsgspecEndpointOptimizer[source]

Optimize endpoints that are parsed with Msgspec.

classmethod optimize_endpoint(metadata: EndpointMetadata) None[source]

Does nothing for msgspec.

class dmr.plugins.msgspec.schema.MsgspecSchemaGenerator[source]

Generates JSON schema for msgspec objects.

classmethod get_schema(model: Any, ref_template: str, *, used_for_response: bool = False) tuple[dict[str, Any], dict[str, Any]] | None[source]

Proxies the JSON schema generation to msgspec itself.

classmethod schema_name(model: Any) str | None[source]

Return a schema name for a model, if it exists.

class dmr.plugins.msgspec.serializer.ToJsonKwargs[source]

Custom deserializer API options, taken by msgspec.to_builtins().

class dmr.plugins.msgspec.serializer.ToModelKwargs[source]

Custom serializer API options, taken by msgspec.convert().