Public API#
Controller#
- class dmr.controller.Controller(**kwargs)[source]#
Bases:
View,Generic[_SerializerT_co]Defines API views as controllers.
Controller is a
django.views.generic.base.Viewsubclass 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]
- exclude_validate_responses#
Set of status codes that we don’t validate, even when
validate_responsesis enabled. Useful for errors like500that can be raised from anywhere and that you might not want to describe.- Type:
ClassVar[collections.abc.Set[http.HTTPStatus] | 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_responsesvalue. 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 includesmeta.- 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]]
- validate_negotiation#
Should we validate that returned response’s
Content-Typeheader matches the one that we inferred in the negotiation process?- Type:
ClassVar[bool | None]
- 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 ofdmr.security.AsyncAuth. Set it toNoneto disable auth of this controller.- Type:
ClassVar[collections.abc.Sequence[dmr.security.base.SyncAuth] | collections.abc.Sequence[dmr.security.base.AsyncAuth] | None]
- throttling#
Sequence of throttle instances to be used. Sync controllers must use instances of
dmr.throttling.SyncThrottle. Async controllers must use instances ofdmr.throttling.AsyncThrottle. Set it toNoneto disable throttling of this controller.- Type:
ClassVar[collections.abc.Sequence[dmr.throttling.base.SyncThrottle] | collections.abc.Sequence[dmr.throttling.base.AsyncThrottle] | None]
- throttling_allow_unsafe_cache#
Should this controller allow unsafe throttle Django cache backends?
- Type:
ClassVar[bool | typing_extensions.sentinel | 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 or exact
api_endpointsinstances.- 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
Trueby default.- Type:
ClassVar[bool]
- summary#
A short summary of what this path item does.
- Type:
ClassVar[_StrOrPromise | None]
- description#
A verbose explanation of the path item behavior.
- Type:
ClassVar[_StrOrPromise | None]
- servers#
An alternative servers array to service this path item.
- Type:
ClassVar[collections.abc.Sequence[dmr.openapi.objects.server.Server] | None]
- ignore_from_spec#
If set to
True, all endpoints from this controller would not be added to the final OpenAPI spec.- Type:
ClassVar[bool]
- request#
Current
HttpRequestinstance.
- args#
Path positional parameters of the request.
- 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_schema(path: str, pattern: URLPattern, context: OpenAPIContext, router: Router) PathItem | None[source]#
Generate OpenAPI spec for path items.
Changed in version 0.13.0:
Renamed from
get_path_itemtoget_schemaNow allows to return
Noneto ignore whole path items from OpenAPI schema
- 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_allowedraises 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
optionsyou should define our ownmetamethod:>>> 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
metamethod is not provided for you. If you want to supportOPTIONShttp 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 setheadmethod 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. Is an alias for
to_responsemethod with a siglightly different signature and semantics.Should be always used instead of using raw
django.http.HttpResponseobjects. Does the usual validation, no “second validation” problem exists.
- to_response(raw_data: Any, *, status_code: HTTPStatus | None = None, headers: Mapping[str, str] | None = None, cookies: Mapping[str, NewCookie] | 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.HttpResponseobjects. 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, router: Router) 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, 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], validate_negotiation: bool, auth: list[SyncAuth | AsyncAuth] | None, throttling_before_auth: tuple[SyncThrottle | AsyncThrottle, ...] | None, throttling_after_auth: tuple[SyncThrottle | AsyncThrottle, ...] | None, throttling_allow_unsafe_cache: bool | None, exclude_validate_responses: frozenset[HTTPStatus], no_validate_http_spec: frozenset[HttpSpec], allowed_http_methods: frozenset[str], semantic_responses: bool, exclude_semantic_responses: frozenset[HTTPStatus], validate_events: bool, summary: _StrOrPromise | None, description: _StrOrPromise | None, tags: list[str] | None, operation_id: str | None, deprecated: bool, external_docs: ExternalDocumentation | None, callbacks: dict[str, Callback | Reference] | None, servers: list[Server] | None, ignore_from_spec: bool)[source]#
Base class for common endpoint metadata.
- type_annotations#
Unmodified unnotations of the endpoint function, returned by the resolution method.
- 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.
- validate_responses#
Do we have to run runtime validation of responses for this endpoint? Already resolved from the global setting, the controller, and the endpoint.
- Type:
- modification#
Default modifications that are applied to the returned data. Can be
None, when@validateis used.- Type:
- 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.
- renderers#
List of instances to be used for this endpoint to render response’s body. All instances must be of subtypes of
Renderer.
- validate_negotiation#
Should we validate that returned response’s
Content-Typeheader matches the one that we inferred in the negotiation process?- Type:
- 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 ofdmr.security.AsyncAuth. When set it toNoneit means that auth is disabled for this endpoint.
- throttling#
Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of
dmr.throttling.SyncThrottle. Async endpoints must use instances ofdmr.throttling.AsyncThrottle. Set it toNoneto disable throttling of this endpoint.- Type:
tuple[SyncThrottle | AsyncThrottle, …] | None
- throttling_before_auth#
Sequence of throttle instances to be used before auth checks.
- Type:
tuple[SyncThrottle | AsyncThrottle, …] | None
- throttling_after_auth#
Sequence of throttle instances to be used after auth checks.
- Type:
tuple[SyncThrottle | AsyncThrottle, …] | None
- throttling_allow_unsafe_cache#
Should this endpoint allow unsafe throttle Django cache backends?
- Type:
bool | None
- exclude_validate_responses#
Set of status codes that we don’t validate, even when
validate_responsesis enabled.- Type:
- no_validate_http_spec#
Set of checks that user wants to disable for validation in this endpoint.
- allowed_http_methods#
Set of extra HTTP methods that are allowed for this endpoint.
- exclude_semantic_responses#
Set of semantic responses that user wants to disable.
- Type:
- validate_events#
Should this endpoint validate events? If not set, defaults to the
validate_responsesvalue. This value only matters if the response will be a streaming response that supports event validation.- Type:
- summary#
A short summary of what the operation does.
- Type:
_StrOrPromise | None
- description#
A verbose explanation of the operation behavior.
- Type:
_StrOrPromise | None
- tags#
A list of tags for API documentation control. Used to group operations in OpenAPI documentation.
- 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.
- 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.
- ignore_from_spec#
If set to
True, this endpoint would not be added to the final OpenAPI spec.- Type:
methodcan be a custom name, not specified inhttp.HTTPMethodenum, whenallowed_http_methodsis used for endpoint definition. This might be useful for cases like when you need to define a method likequery, 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[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
Controllerlikechecks=. And you can subclassEndpointMetadatato also containchecksfield and override this method to also include response specs from this field.Define
semantic_responsestoFalseon settings or controller level to disable semantic responses collection.
- @dmr.endpoint.modify(*, error_handler: None = None, status_code: HTTPStatus | None = None, headers: Mapping[str, NewHeader | HeaderSpec] | None = None, cookies: Mapping[str, NewCookie | CookieSpec] | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | None = frozenset(), 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, validate_negotiation: bool | None = None, auth: Sequence[Never] | None = (), throttling: Sequence[Never] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None) ModifyAnyCallable[source]#
- @dmr.endpoint.modify(*, error_handler: AsyncErrorHandler | None = None, status_code: HTTPStatus | None = None, headers: Mapping[str, NewHeader | HeaderSpec] | None = None, cookies: Mapping[str, NewCookie | CookieSpec] | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | None = frozenset(), 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, validate_negotiation: bool | None = None, auth: Sequence[AsyncAuth] | None = (), throttling: Sequence[AsyncThrottle] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None) ModifyAsyncCallable
- @dmr.endpoint.modify(*, error_handler: SyncErrorHandler | None = None, status_code: HTTPStatus | None = None, headers: Mapping[str, NewHeader | HeaderSpec] | None = None, cookies: Mapping[str, NewCookie | CookieSpec] | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | None = frozenset(), 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, validate_negotiation: bool | None = None, auth: Sequence[SyncAuth] | None = (), throttling: Sequence[SyncThrottle] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None) ModifySyncCallable
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.
exclude_validate_responses – Set of status codes that we don’t validate, even when
validate_responsesis enabled. Useful for errors like500that can be raised from anywhere and that you might not want to describe.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_responsesvalue. 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.validate_negotiation – Should we validate that returned response’s
Content-Typeheader matches the one that we inferred in the negotiation process?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 ofdmr.security.AsyncAuth. Set it toNoneto disable auth for this endpoint.throttling – Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of
dmr.throttling.SyncThrottle. Async endpoints must use instances ofdmr.throttling.AsyncThrottle. Set it toNoneto disable throttling of this endpoint.throttling_allow_unsafe_cache – Should this endpoint allow unsafe throttle Django cache backends?
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.
ignore_from_spec – If set to
True, this endpoint would not be added to the final OpenAPI spec.
- Returns:
The same function with
__dmr_payload__payload instance.
Warning
Do not disable
validate_responsesunless this is performance critical for you!
- @dmr.endpoint.validate(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | None = frozenset(), 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, validate_negotiation: bool | None = None, auth: Sequence[Never] | None = (), throttling: Sequence[Never] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None) Callable[[Callable[[_ParamT], _ResponseT]], Callable[[_ParamT], _ResponseT]][source]#
- @dmr.endpoint.validate(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: AsyncErrorHandler | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | None = frozenset(), 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, validate_negotiation: bool | None = None, auth: Sequence[AsyncAuth] | None = (), throttling: Sequence[AsyncThrottle] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None) Callable[[Callable[[_ParamT], Awaitable[HttpResponseBase]]], Callable[[_ParamT], Awaitable[HttpResponseBase]]]
- @dmr.endpoint.validate(response: ResponseSpec, /, *responses: ResponseSpec, error_handler: SyncErrorHandler | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | None = frozenset(), 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, validate_negotiation: bool | None = None, auth: Sequence[SyncAuth] | None = (), throttling: Sequence[SyncThrottle] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None) Callable[[Callable[[_ParamT], HttpResponseBase]], Callable[[_ParamT], HttpResponseBase]]
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.pyfile: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.
exclude_validate_responses – Set of status codes that we don’t validate, even when
validate_responsesis enabled. Useful for errors like500that can be raised from anywhere and that you might not want to describe.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_responsesvalue. 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.validate_negotiation – Should we validate that returned response’s
Content-Typeheader matches the one that we inferred in the negotiation process?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 ofdmr.security.AsyncAuth. Set it toNoneto disable auth for this endpoint.throttling – Sequence of throttle instances to be used for this endpoint. Sync endpoints must use instances of
dmr.throttling.SyncThrottle. Async endpoints must use instances ofdmr.throttling.AsyncThrottle. Set it toNoneto disable throttling of this endpoint.throttling_allow_unsafe_cache – Should this controller allow unsafe throttle Django cache backends?
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.
ignore_from_spec – If set to
True, this endpoint would not be added to the final OpenAPI spec.
- Returns:
The same function with
__dmr_payload__payload instance.
Warning
Do not disable
validate_responsesunless this is performance critical for you!
- @dmr.endpoint.request_endpoint(request: HttpRequest, *, strict: Literal[True]) Endpoint[source]#
- @dmr.endpoint.request_endpoint(request: HttpRequest, *, strict: bool = False) Endpoint | None
Return an instance of the
Endpointthat was used for this request.When strict is passed and request has no endpoint, we raise
AttributeError. This can happen for405responses, for example. They don’t have endpoints. All others do.Added in version 0.7.0.
Validation#
- class dmr.validation.ModifyEndpointPayload(*, summary: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | 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, validate_negotiation: bool | None = None, auth: Sequence[SyncAuth] | Sequence[AsyncAuth] | None = (), throttling: Sequence[SyncThrottle] | Sequence[AsyncThrottle] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, 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: _StrOrPromise | None = None, description: _StrOrPromise | 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, ignore_from_spec: bool | None = None, validate_responses: bool | None = None, exclude_validate_responses: Set[HTTPStatus] | 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, validate_negotiation: bool | None = None, auth: Sequence[SyncAuth] | Sequence[AsyncAuth] | None = (), throttling: Sequence[SyncThrottle] | Sequence[AsyncThrottle] | None = (), throttling_allow_unsafe_cache: bool | sentinel | None = EMPTY, responses: list[ResponseSpec])[source]#
Payload created by
@validate.
Serialization#
- class dmr.serializer.BaseSerializer[source]#
Abstract base class for data serialization.
What serializer does?
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
It provides validation for raw python data, see
dmr.serializer.BaseSerializer.from_python()methodIt provides serialization for related complex utility objects like validation errors and responses that don’t have
.contentattribute. For example: file and sse responses
- validation_error#
Exception type that is used for validation errors. Required to be set in subclasses.
- 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]]
- schema_generator#
Generates schema and schema names for the OpenAPI.
- Type:
ClassVar[type[dmr.serializer.BaseSchemaGenerator]]
- 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, extra_namespace: Mapping[str, Any] | None = None) Any[source]#
Parse unstructured data from python primitives into model.
Raises
cls.validation_errorwhen 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.
extra_namespace – Optional namespace to load type annotations from. It is useful, when using stringified or lazy type annotations.
- Returns:
Structured and validated data.
Changed in version 0.13.0: Added extra_namespace parameter.
- 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.ValidationErrortype. That can’t be converted to a simpler error message easily.- Returns:
Simple python object - exception converted to json.
- 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_errorwhen 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.
- 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: Iterable[URLPattern | URLResolver | URLExternal] = (), *, tags: Sequence[str] | None = None, deprecated: bool = False, ignore_from_spec: bool = False)[source]#
Collection of HTTP routes for REST framework.
- prefix#
URL prefix for all routes (e.g., ‘api/v1/’). Defaults to empty string
''.
- urls#
Sequence of URL patterns and resolvers.
- tags#
Optional sequence of tags to group operations in OpenAPI. These are merged with endpoint-level tags.
- deprecated#
Optional flag to mark all operations as deprecated. Combines with endpoint-level deprecated flag using OR logic.
- ignore_from_spec#
If set to
True, all routes from this router are excluded from the generated OpenAPI specification. Runtime URL routing is not affected.
Note
tags and deprecated is not applied to external urls’ metadata. It is always included as-is.
Changed in version 0.7.0: Added tags and deprecated parameters.
Changed in version 0.13.0: Now you can pass
external_path()objects in urls. Also accept anycollections.abc.Sequenceas tags. urls parameter is now optional.Changed in version 0.15.0: Added ignore_from_spec parameter.
- 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.
- include(router: Router, *, namespace: str | None = None, app_name: str | None = None) None[source]#
Include a router’s URLs under a given app name and namespace.
Added in version 0.13.0.
- metadata_for(pattern: str) RouterMetadata[source]#
Returns applied nested metadata from all router layers.
- Raises:
KeyError – if pattern is not found.
Added in version 0.15.0.
- to_urlpatterns(*, namespace: str | None = None, app_name: str | None = None) URLResolver[source]#
Convert router instance into
urlpatternsinclude API.Can be used to include one router into another. Or to include a router into the final
urlpatternslist.Automatically uses our own faster
path()function.Added in version 0.14.0.
- 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
Acceptdoes not match any renderer, the first configured renderer is used. For non-matching paths, Django’s defaultpage_not_foundhandler 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
renderersfrom 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
Acceptdoes not match any renderer, the first configured renderer is used. For non-matching paths, Django’s defaultserver_errorhandler 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
renderersfrom settings.
- dmr.routing.path(route: _StrOrPromise, view: Callable[[...], HttpResponseBase | Coroutine[Any, Any, HttpResponseBase]], kwargs: dict[str, Any] | None = None, name: str | None = None) URLPattern[source]#
- 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.
- dmr.routing.external_path(route: _StrOrPromise, view: Callable[[...], HttpResponseBase | Coroutine[Any, Any, HttpResponseBase]], *, openapi: PathItem | None, kwargs: dict[str, Any] | None = None, name: str | None = None) _URLExternal[source]#
Add an external path onto the DMR routing system.
Automatically uses our own faster
path()function.- Parameters:
route – String route for the view.
view – Function or class view, supports both sync and async callables.
openapi – OpenAPI metadata to show in the spec. Or
Noneto hide this endpoint.kwargs – Init kwargs for the view.
name – Name to resolve this URL.
Important
This function only works when including a URL into our own
Routerobjects, not into the Django ownurlpatterns.Django check
urls.E004covers this statically.See External views for more info.
Added in version 0.13.0.
Meta mixins#
- class dmr.options_mixins.MetaMixin[source]#
Mixin that provides default
metamethod orOPTIONShttp method.Use it for sync controllers.
It just returns the list of allowed methods. Use it as a mixin with the
dmr.controller.Controllertype:>>> 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
OPTIONShttp method.
- class dmr.options_mixins.AsyncMetaMixin[source]#
Mixin that provides default
metamethod orOPTIONShttp method.Use it for async controllers.
It just returns the list of allowed methods. Use it as a mixin with the
dmr.controller.Controllertype:>>> 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
OPTIONShttp method.
Exceptions#
- final exception dmr.exceptions.UnsolvableAnnotationsError[source]#
Bases:
ExceptionRaised 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]#
Bases:
ExceptionRaised when user didn’t specify some required endpoint metadata.
- final exception dmr.exceptions.DataParsingError[source]#
Bases:
ExceptionRaised when input data cannot be parsed.
- final exception dmr.exceptions.RequestSerializationError[source]#
Bases:
ExceptionRaised when we fail to parse some request part.
- final exception dmr.exceptions.ResponseSchemaError[source]#
Bases:
ExceptionRaised 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]#
Bases:
ExceptionRaised 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]#
Bases:
ExceptionRaised when client provides wrong
Acceptheader.
- final exception dmr.exceptions.NotAuthenticatedError(msg: str | Promise | None = None, *, headers: dict[str, str] | None = None)[source]#
Bases:
ExceptionRaised when we fail to authenticate a user.
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.Anybecause 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.
- 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.
- dmr.types.safe_typevar(typevar_name: str, *, stacklevel: int = 1, globalns: dict[str, Any] | None = None) Any[source]#
Typing utility to allow passing
typing.TypeVarsafely.By default
mypyand other type-checkers would raise a typing error on a code like this:>>> from typing import TypeVar >>> from http import HTTPStatus >>> from dmr import validate, ResponseSpec >>> _ModelT = TypeVar('_ModelT') >>> validate( ... ResponseSpec( ... # In traditional Python typing spec, it is not allowed ... # to use type var in this context: ... _ModelT, # type: ignore[misc] ... status_code=HTTPStatus.OK, ... ), ... ) <function ...>
But, this function can help with this problem with no type errors:
>>> validate( ... ResponseSpec( ... safe_typevar('_ModelT'), ... status_code=HTTPStatus.OK, ... ), ... ) <function ...>
- Parameters:
typevar_name – TypeVar name to find in the globals.
stacklevel – Levels of function frames to get globals from.
globalns – Explicit
globalsnamespace. Has a higher priority than stacklevel.
- Raises:
KeyError – If typevar_name is not found in globalns.
Added in version 0.14.0.
Decorators#
- dmr.decorators.dispatch_decorator(func: Callable[[...], Any]) Callable[[_TypeT], _TypeT][source]#
Special helper to decorate class-based view’s
dispatchmethod.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
MyControllerto 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 decoratorDanger
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 useendpoint_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.
Use it with “raw” endpoints that return regular data, not
django.http.HttpResponse.Basically, all endpoints that can be decorated with
modify().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()-django.views.decorators.debug.sensitive_post_parameters()- and any other default or custom django decoratorWarning
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_requiredwill 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/toolsThis type, in contrast to a regular
RequestFactory, setscontent-typeasapplication/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
DMRRequestFactorybut for ASGI environment.Uses the exactly the same API.
- 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, setscontent-typeasapplication/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
asyncAPI. Requires you toawaitcalls to.get,.post, etc.
Auth#
- dmr.test.disabled_auth(controller_cls: type[Controller[BaseSerializer]], *, request: HttpRequest, user: AbstractBaseUser, auth: SyncAuth | AsyncAuth | None = None) Generator[None][source]#
Temporarily disable all auth for the endpoint for testing.
- Parameters:
controller_cls – Controller whose endpoint is under test.
request – HTTP method of the endpoint to target.
user – User to be used for this request.
auth – Optional auth instance to be used for this request. It is set as
__dmr_auth__attribute, like our regular auth does. Usedmr.security.request_auth()to get it from request.
Added in version 0.13.0.
Throttling#
- dmr.test.reduced_throttling(controller_cls: type[Controller[BaseSerializer]], *, method: HTTPMethod | str, max_requests: int = 2, rate: Rate = Rate.hour, when: Literal['any', 'before_auth', 'after_auth'] = 'any') Generator[SyncThrottle | AsyncThrottle][source]#
Temporarily lower an endpoint’s first throttle so a test can reach it.
Replaces the first throttle of the chosen when with a copy limited to max_requests, so a few real requests trip it instead of the configured rate. Only the endpoint under test is affected; the original throttling is restored on exit. Yields the reduced throttle, whose max_requests tells you how many allowed requests to send before the next one is rejected.
The copy also has its window widened to rate, because lowering just max_requests is not enough for short windows: a
2/secondthrottle would reset between the driven requests and the endpoint would never be rejected. With an hour-long window every request a test sends lands inside the same window.- Parameters:
controller_cls – Controller whose endpoint is under test.
method – HTTP method of the endpoint to target.
max_requests – Limit the throttle is lowered to (
2by default). The window is always widened to one hour, see above.rate – Time window to narrow to (
hourby default).when – When to run the throttle –
'before_auth'(checked before auth, e.g. by IP),'after_auth'(per-user, needs an authenticated request), or'any'(default, the first one checked).
Added in version 0.12.0.
- dmr.test.assert_throttled(response: HttpResponse, *, throttle: SyncThrottle | AsyncThrottle | None = None) None[source]#
Assert that a response was rejected by throttling.
Collapses the repeated
429status, header and error-body checks into a single call:# Just the status + `ratelimit` error: assert_throttled(response) # Also asserts all the headers from the throttle instance exist: assert_throttled(response, throttle=your_throttled_instance)
- Parameters:
response – The response to check.
throttle – Throttle that rejected the request. When given, every header its providers report must be present in the response.
throttle instance defines which headers are reported depends on the header provider’s class:
XRateLimit,RateLimitIETFDraft, or your own provider.Added in version 0.12.0.
- dmr.test.assert_throttling(controller_cls: type[Controller[BaseSerializer]], request_factory: Callable[[], HttpRequest], *, max_requests: int = 2, rate: Rate = Rate.hour, when: Literal['any', 'before_auth', 'after_auth'] = 'any', success_status: HTTPStatus | None = None) tuple[HttpResponse, SyncThrottle][source]#
Reduce the first throttle, drive requests, and assert the
429.Sends max_requests allowed requests, then one that is rejected, and checks it with
assert_throttled()– including every header the reduced throttle reports. Returns the429response, so you can make further assertions on it.Parameters when and rate are passed down to
reduced_throttling()andassert_throttled().Added in version 0.12.0.
- async dmr.test.assert_async_throttling(controller_cls: type[Controller[BaseSerializer]], request_factory: Callable[[], HttpRequest], *, max_requests: int = 2, rate: Rate = Rate.hour, when: Literal['any', 'before_auth', 'after_auth'] = 'any', success_status: HTTPStatus | None = None) tuple[HttpResponse, AsyncThrottle][source]#
Async version of
assert_throttling().Added in version 0.12.0.
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:
- to_model_kwargs#
Dictionary of kwargs that will be passed to model deserialization callbacks.
- Type:
- 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, extra_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
pydanticsupports. Examples:dict[str, int]andBaseModelsubtypes.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.
extra_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.
Changed in version 0.13.0: Added rebuild_namespace parameter was renamed to be extra_namespace.
- 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.
- class dmr.plugins.pydantic.PydanticFastSerializer[source]#
Fast pydantic serializer for cases when you only work with json.
Does not use
parserandrendererpassed objects, does not usedmr.plugins.pyndatic.PydanticSerializer.serialize_hookanddmr.plugins.pyndatic.PydanticSerializer.deserialize_hookmethod.Is built for optimizations only, use with caution.
Only works with
application/jsoncontent 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.
- 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.
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:
- to_model_kwargs#
Dictionary of kwargs that will be passed to model deserialization callbacks.
- Type:
- 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, extra_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
msgspecsupports. Examples:dict[str, int]andBaseModelsubtypes.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.
extra_namespace – Not used currently.
- Returns:
Structured and validated data.
- Raises:
msgspec.ValidationError – When parsing can’t be done.
Changed in version 0.13.0: Added extra_namespace parameter.
- 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.
- 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().
OpenAPI#
Main OpenAPI object:
- class dmr.openapi.openapi.OpenAPI(*, info: Info, openapi: str, json_schema_dialect: str | None = None, servers: list[Server] | None = None, paths: dict[str, PathItem] | None = None, webhooks: dict[str, PathItem | Reference] | None = None, components: Components | None = None, security: list[dict[str, list[str]]] | None = None, tags: list[Tag] | None = None, external_docs: ExternalDocumentation | None = None)[source]#
This is the root object of the OpenAPI document.
Changed in version 0.13.0: Moved from
dmr.openapi.objects.OpenAPItodmr.openapi.openapi.OpenAPI.- convert(*, skip_validation: bool = False) dict[str, Any][source]#
Convert the object to OpenAPI schema dictionary.
Runs validation if
'django-modern-rest[openapi]'is installed and skip_validation is falsy.The converted dictionary is cached on this instance and reused by subsequent calls. Finish modifying the schema before the first call and treat the returned dictionary as read-only. Build a new schema instance to reflect later changes.
Skipping validation does not prevent a later call from validating the cached dictionary.
Parts:
- class dmr.openapi.objects.Components(*, schemas: dict[str, Schema] | None = None, responses: dict[str, Response | Reference] | None = None, parameters: dict[str, Parameter | Reference] | None = None, examples: dict[str, Example | Reference] | None = None, request_bodies: dict[str, RequestBody | Reference] | None = None, headers: dict[str, Header | Reference] | None = None, security_schemes: dict[str, SecurityScheme | Reference] | None = None, links: dict[str, Link | Reference] | None = None, callbacks: dict[str, Callback | Reference] | None = None, path_items: dict[str, PathItem | Reference] | None = None)[source]#
Holds a set of reusable objects for different aspects of the OAS.
All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.
- class dmr.openapi.objects.Contact(*, name: str | None = None, url: str | None = None, email: str | None = None)[source]#
Contact information for the exposed API.
- class dmr.openapi.objects.Discriminator(*, property_name: str, mapping: dict[str, str] | None = None)[source]#
Discriminator Object.
When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the document of an alternative schema based on the value associated with it.
- class dmr.openapi.objects.Encoding(*, content_type: str | None = None, headers: dict[str, Header | Reference] | None = None, style: str | None = None, explode: bool | None = None, allow_reserved: bool | None = None)[source]#
A single encoding definition applied to a single schema property.
- class dmr.openapi.objects.Example(*, summary: str | None = None, description: str | None = None, value: Any | None = None, external_value: str | None = None)[source]#
Example Object.
In all cases, the example value is expected to be compatible with the type schema of its associated value. Tooling implementations MAY choose to validate compatibility automatically, and reject the example value(s) if incompatible.
- class dmr.openapi.objects.ExternalDocumentation(*, url: str, description: str | None = None)[source]#
Allows referencing an external resource for extended documentation.
- class dmr.openapi.objects.Header(*, schema: Reference | Schema | None = None, description: str | None = None, required: bool | None = None, deprecated: bool | None = None, style: str | None = None, explode: bool | None = None, example: Any | None = None, examples: dict[str, Example | Reference] | None = None, content: dict[str, MediaType] | None = None)[source]#
Header Object.
The Header Object follows the structure of the Parameter Object with the following changes: All traits that are affected by the location MUST be applicable to a location of header (for example, style).
- class dmr.openapi.objects.Info(*, title: str, version: str, summary: str | None = None, description: Reference | str | None = None, terms_of_service: str | None = None, contact: Contact | None = None, license: License | None = None)[source]#
The Info object provides metadata about the API.
The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience.
- class dmr.openapi.objects.License(*, name: str, identifier: str | None = None, url: str | None = None)[source]#
License information for the exposed API.
- class dmr.openapi.objects.Link(*, operation_ref: str | None = None, operation_id: str | None = None, parameters: dict[str, Any] | None = None, request_body: Any | None = None, description: str | None = None, server: Server | None = None)[source]#
The Link object represents a possible design-time link for a response.
The presence of a link does not guarantee the caller’s ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations.
- final class dmr.openapi.objects.MediaTypeMetadata(*, example: Any | None = None, examples: dict[str, Example | Reference] | None = None, encoding: dict[str, Encoding] | None = None, item_encoding: Encoding | None = None, prefix_encoding: Encoding | None = None)[source]#
Media type metadata to be set on a request body.
- class dmr.openapi.objects.MediaType(*, schema: Reference | Schema | None = None, example: Any | None = None, examples: dict[str, Example | Reference] | None = None, encoding: dict[str, Encoding] | None = None, item_schema: Reference | Schema | None = None, item_encoding: Encoding | None = None, prefix_encoding: Encoding | None = None)[source]#
Media Type Object.
Each Media Type Object provides schema and examples for the media type identified by its key.
- class dmr.openapi.objects.OAuthFlow(*, authorization_url: str | None = None, token_url: str | None = None, refresh_url: str | None = None, scopes: dict[str, str] | None = None)[source]#
Configuration details for a supported OAuth Flow.
- class dmr.openapi.objects.OAuthFlows(*, implicit: OAuthFlow | None = None, password: OAuthFlow | None = None, client_credentials: OAuthFlow | None = None, authorization_code: OAuthFlow | None = None)[source]#
Allows configuration of the supported OAuth Flows.
- class dmr.openapi.objects.Operation(*, tags: list[str] | None = None, summary: str | None = None, description: str | None = None, external_docs: ExternalDocumentation | None = None, operation_id: str | None = None, parameters: list[Parameter | Reference] | None = None, request_body: RequestBody | Reference | None = None, responses: Responses | None = None, callbacks: dict[str, Callback | Reference] | None = None, deprecated: bool = False, security: list[SecurityRequirement] | None = None, servers: list[Server] | None = None)[source]#
Describes a single API operation on a path.
- class dmr.openapi.objects.ParameterMetadata(*, description: str | None = None, deprecated: bool = False, allow_empty_value: bool | None = None, style: str | None = None, explode: bool | None = None, allow_reserved: bool | None = None, example: Any | None = None, examples: dict[str, Example | Reference] | None = None)[source]#
Describes metadata for a single operation parameter.
- class dmr.openapi.objects.Parameter(*, description: str | None = None, deprecated: bool = False, allow_empty_value: bool | None = None, style: str | None = None, explode: bool | None = None, allow_reserved: bool | None = None, example: Any | None = None, examples: dict[str, Example | Reference] | None = None, name: str, param_in: Annotated[str, FieldInfo(annotation=NoneType, required=True, alias='in', alias_priority=2)], schema: Reference | Schema | None = None, content: dict[str, MediaType] | None = None, required: bool = False)[source]#
Bases:
ParameterMetadataDescribes a single operation parameter.
- class dmr.openapi.objects.PathItem(*, ref: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$ref', alias_priority=2)] = None, summary: str | None = None, description: str | None = None, get: Operation | None = None, put: Operation | None = None, post: Operation | None = None, delete: Operation | None = None, options: Operation | None = None, head: Operation | None = None, patch: Operation | None = None, trace: Operation | None = None, query: Operation | None = None, servers: list[Server] | None = None, parameters: list[Parameter | Reference] | None = None, additional_operations: dict[str, Operation] | None = None)[source]#
Describes the operations available on a single path.
A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available.
- class dmr.openapi.objects.Reference(*, ref: Annotated[str, FieldInfo(annotation=NoneType, required=True, alias='$ref', alias_priority=2)], summary: str | None = None, description: str | None = None)[source]#
A simple object to allow referencing other components in the document.
The $ref string value contains a URI RFC3986, which identifies the location of the value being referenced.
- class dmr.openapi.objects.RequestBody(*, content: dict[str, MediaType], description: str | None = None, required: bool = True)[source]#
Describes a single request body.
- class dmr.openapi.objects.Response(*, description: str | None = None, headers: dict[str, Header | Reference] | None = None, content: dict[str, MediaType] | None = None, links: dict[str, Link | Reference] | None = None)[source]#
Describes a single response from an API Operation.
Including design-time, static links to operations based on the response.
- class dmr.openapi.objects.Schema(*, all_of: list[Reference | Schema] | None = None, any_of: list[Reference | Schema] | None = None, one_of: list[Reference | Schema] | None = None, schema_not: Annotated[Reference | Schema | None, FieldInfo(annotation=NoneType, required=True, alias='not', alias_priority=2)] = None, schema_if: Annotated[Reference | Schema | None, FieldInfo(annotation=NoneType, required=True, alias='if', alias_priority=2)] = None, schema_then: Annotated[Reference | Schema | None, FieldInfo(annotation=NoneType, required=True, alias='then', alias_priority=2)] = None, schema_else: Annotated[Reference | Schema | None, FieldInfo(annotation=NoneType, required=True, alias='else', alias_priority=2)] = None, dependent_schemas: dict[str, Reference | Schema] | None=None, prefix_items: list[Reference | Schema] | None = None, items: Reference | Schema | bool | None = None, contains: Reference | Schema | None = None, properties: dict[str, Reference | Schema] | None=None, pattern_properties: dict[str, Reference | Schema] | None=None, additional_properties: Reference | Schema | bool | None = None, property_names: Reference | Schema | None = None, unevaluated_items: Reference | Schema | None = None, unevaluated_properties: Reference | Schema | None = None, type: OpenAPIType | list[OpenAPIType] | None = None, enum: list[Any] | None = None, const: Any | None = None, multiple_of: float | None = None, maximum: float | None = None, exclusive_maximum: float | None = None, minimum: float | None = None, exclusive_minimum: float | None = None, max_length: int | None = None, min_length: int | None = None, pattern: str | None = None, max_items: int | None = None, min_items: int | None = None, unique_items: bool | None = None, max_contains: int | None = None, min_contains: int | None = None, max_properties: int | None = None, min_properties: int | None = None, required: list[str] = <factory>, dependent_required: dict[str, list[str]] | None=None, format: OpenAPIFormat | None = None, content_encoding: str | None = None, content_media_type: str | None = None, content_schema: Reference | Schema | None = None, title: str | None = None, description: str | None = None, default: Any | None = None, deprecated: bool | None = None, read_only: bool | None = None, write_only: bool | None = None, examples: list[Any] | None = None, discriminator: Discriminator | None = None, xml: XML | None = None, external_docs: ExternalDocumentation | None = None, example: Any | None = None, dynamic_anchor: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$dynamicAnchor', alias_priority=2)] = None, dynamic_ref: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$dynamicRef', alias_priority=2)] = None, ref: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$ref', alias_priority=2)] = None, anchor: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$anchor', alias_priority=2)] = None, comment: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$comment', alias_priority=2)] = None, schema_uri: Annotated[str | None, FieldInfo(annotation=NoneType, required=True, alias='$schema', alias_priority=2)] = None, defs: Annotated[dict[str, Reference | Schema] | None, FieldInfo(annotation=NoneType, required=True, alias='$defs', alias_priority=2)] = None)[source]#
The Schema Object allows the definition of input and output data types.
These types can be objects, but also primitives and arrays. Unless stated otherwise, the property definitions follow those of JSON Schema and do not add any additional semantics. Where JSON Schema indicates that behavior is defined by the application (e.g. for annotations), OAS also defers the definition of semantics to the application consuming the OpenAPI document.
- class dmr.openapi.objects.SecurityScheme(*, type: Literal['apiKey', 'http', 'mutualTLS', 'oauth2', 'openIdConnect'], description: str | None = None, name: str | None = None, security_scheme_in: Annotated[Literal['query', 'header', 'cookie'] | None, FieldInfo(annotation=NoneType, required=True, alias='in', alias_priority=2)] = None, scheme: str | None = None, bearer_format: str | None = None, flows: OAuthFlows | None = None, open_id_connect_url: str | None = None)[source]#
Defines a security scheme that can be used by the operations.
Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), mutual TLS (use of a client certificate), OAuth2’s common flows (implicit, password, client credentials and authorization code) as defined in RFC6749, and OpenID Connect Discovery. Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. Recommended for most use cases is Authorization Code Grant flow with PKCE.
- class dmr.openapi.objects.Server(*, url: str, description: str | None = None, variables: dict[str, ServerVariable] | None = None)[source]#
An object representing a Server.
- class dmr.openapi.objects.ServerVariable(*, default: str, enum: list[str] | None = None, description: str | None = None)[source]#
An object representing a Server Variable for server URL template.
- class dmr.openapi.objects.Tag(*, name: str, description: str | None = None, external_docs: ExternalDocumentation | None = None)[source]#
Adds metadata to a single tag that is used by the Operation object.
It is not mandatory to have a Tag object per tag defined in the Operation object instances.
- class dmr.openapi.objects.XML(*, name: str | None = None, namespace: str | None = None, prefix: str | None = None, attribute: bool = False, wrapped: bool = False)[source]#
A metadata object that allows for more fine-tuned XML model definitions.
When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be used to add that information.
OpenAPI Core#
- class dmr.openapi.core.merger.ConfigMerger(context: OpenAPIContext)[source]#
Merges OpenAPI configuration with generated paths and components.
This class is responsible for combining the OpenAPI configuration from the context with the generated paths and components to create a complete OpenAPI specification object.
- class dmr.openapi.core.registry.SchemaRegistry[source]#
Registry for
Schemas.- get_reference(schema_name: str | None, annotation: Any | sentinel = EMPTY) Reference | None[source]#
Get registered reference.
- maybe_resolve_reference(reference: Reference | Schema, *, resolution_context: dict[str, Schema] | None = None) Schema[source]#
Resolve reference and return a schema back.
OpenAPI Generators#
- class dmr.openapi.generators.ComponentParserGenerator(_context: OpenAPIContext)[source]#
Generator for OpenAPI
Parameterobjects.- __call__(operation_id: str, pattern: URLPattern, metadata: EndpointMetadata, serializer: type[BaseSerializer]) tuple[RequestBody | Reference | None, list[Parameter | Reference] | None][source]#
Generate parameters from parsers.
- class dmr.openapi.generators.ResponseGenerator(_context: OpenAPIContext)[source]#
Generator for OpenAPI
Responseobjects.- __call__(metadata: EndpointMetadata, serializer: type[BaseSerializer]) dict[str, Response | Reference][source]#
Generate responses from response specs.
- get_schema(response_spec: ResponseSpec, metadata: EndpointMetadata, serializer: type[BaseSerializer], context: OpenAPIContext, *, schema_field_name: Literal['schema', 'item_schema'] = 'schema', used_for_response: bool = True) Response[source]#
Returns the OpenAPI schema for the response.
Can be customized in
ResponseSpecsubclasses.
- class dmr.openapi.generators.SchemaGenerator(_context: OpenAPIContext)[source]#
Generate OpenAPI schemas from different type annotations.
- __call__(annotation: Any, serializer: type['BaseSerializer'], *, used_for_response: bool = False, skip_registration: Literal[True], register_referenced_components: bool = False) Schema[source]#
- __call__(annotation: Any, serializer: type['BaseSerializer'], *, used_for_response: bool = False, skip_registration: bool = False, register_referenced_components: bool = False) Reference | Schema
Get schema for an annotation.
Here’s the algorithm we use:
First, we try to find manually defined overrides for the annotation
If nothing is found, we try to find any existing schema references
Next, we try to get a model schema from a serializer. If it exists, we create an internal reference and return it. The next time it will be returned as a reference, cached.
If nothing worked, we raise an error
- Raises:
UnsolvableAnnotationsError – when we can’t generate an OpenAPI schema from an existing annotation.
- class dmr.openapi.generators.SecuritySchemeGenerator(_context: OpenAPIContext)[source]#
Generator for OpenAPI Security Schemes.
Responsible for processing authentication providers, extracting their security schemes, registering them in the context, and returning the corresponding security requirements for the operation.
- __call__(auth_providers: Sequence[SyncAuth | AsyncAuth] | None, serializer: type[BaseSerializer]) list[SecurityRequirement] | None[source]#
Process auth providers and generate security requirements.
Iterates over the provided authentication providers, registers their security schemes in the global registry, and collects their security usage requirements.
- class dmr.openapi.generators.OperationIdGenerator(_context: OpenAPIContext)[source]#
Generator for unique OpenAPI operation IDs.
The Operation ID builder is responsible for creating unique operation IDs for OpenAPI operations. It uses the explicit
operation_idfrom endpoint metadata if available, otherwise generates one from the HTTP method and path followingRFC 3986specifications. All generated operation IDs are registered in the registry to ensure uniqueness across the OpenAPI specification.- __call__(path: str, suffix: str, metadata: EndpointMetadata, serializer: type[BaseSerializer]) str[source]#
Generate a unique operation ID for an OpenAPI operation.
Uses the explicit
operation_idfrom endpoint metadata if available, otherwise generates one from the HTTP method and path. The operation ID is registered in the registry to ensure uniqueness.
Existing OpenAPI views#
Existing implementations:
- class dmr.openapi.views.ScalarView(**kwargs)[source]#
View for rendering the OpenAPI schema with Scalar.
Renders an interactive HTML page that allows exploring the
OpenAPIspecification using Scalar API Reference.- get(request: HttpRequest) HttpResponse[source]#
Render the OpenAPI schema using Scalar template.
- class dmr.openapi.views.SwaggerView(**kwargs)[source]#
View for rendering the OpenAPI schema with Swagger UI.
Renders an interactive HTML page that allows exploring the
OpenAPIspecification using Swagger UI components.- get(request: HttpRequest) HttpResponse[source]#
Render the OpenAPI schema using Swagger template.
- class dmr.openapi.views.RedocView(**kwargs)[source]#
View for rendering the OpenAPI schema with Redoc.
Renders an interactive HTML page that allows exploring the
OpenAPIspecification using Redoc components.- get(request: HttpRequest) HttpResponse[source]#
Render the OpenAPI schema using Redoc template.
- class dmr.openapi.views.StoplightView(**kwargs)[source]#
View for rendering the OpenAPI schema with Stoplight.
Renders an interactive HTML page that allows exploring the
OpenAPIspecification using Stoplight API Reference.- get(request: HttpRequest) HttpResponse[source]#
Render the OpenAPI schema using Stoplight template.
- class dmr.openapi.views.OpenAPIJsonView(**kwargs)[source]#
View for returning the OpenAPI schema as JSON.
Produces a JSON representation of the
OpenAPIspecification that can be used by API documentation tools and client code generators.- content_type#
Content type of the rendered response. Defaults to
"application/json".- Type:
ClassVar[str]
- get(request: HttpRequest) HttpResponse[source]#
Render the OpenAPI schema as JSON response.
- class dmr.openapi.views.yaml.OpenAPIYamlView(**kwargs)[source]#
View for returning the OpenAPI schema as YAML.
This view mirrors
OpenAPIJsonView, but renders the converted schema usingpyyaml. Produces a YAML representation of theOpenAPIspecification that can be used by API documentation tools and client code generators.- get(request: HttpRequest) HttpResponse[source]#
Render the OpenAPI schema as YAML response.
Base classes:
- class dmr.openapi.views.base.OpenAPIView(**kwargs)[source]#
Base view for serving an OpenAPI schema.
This view extends Django’s
Viewto accept anOpenAPIinstance viaas_view(). The passed schema is stored on the view class and can be rendered in any concrete subclass (for example, as JSON or YAML).- classmethod as_view(schema: OpenAPI, *, skip_validation: bool = False, **initkwargs: Any) Callable[[...], HttpResponseBase][source]#
Create a view function bound to the given OpenAPI schema.