Components

django-modern-rest utilizes component approach to parse all the unstructured things like headers, body, and cookies into a strongly typed and validated model.

To use a component, you can just add it as a parameter to your endpoint method inside a Controller.

How does it work?

  • In import time, when controller is first created, we iterate over all existing endpoints in this class

  • For each endpoint we fetch method annotations and find all ComponentParser objects, they will be treated as component parsers

  • Next, we create a request parsing model during the import time, with all combined fields to be parsed later

  • In runtime, when request is received, we provide the needed data for this single parsing model

  • If everything is ok, we call the needed endpoint with the correct data

  • If there’s a parsing error we raise RequestSerializationError and return a beautiful error message for the user

You can use existing ones or create your own.

Note

All existing components should only be inherited for parsing. If you want to change the implementation details of a component – create a new one from scratch.

You can still delegate parts of the work to existing ones.

What is inside a component?

All components consist of two parts:

  1. The first one is a ComponentParser subclass, which knows how to provide the required data for itself, build OpenAPI schemas and etc. For example: QueryComponent

  2. The second part is a typing.Annotated based annotation that has a component parser instance as metadata. These annotations will be used by the end users. For example, Query

Browse components

Query

Parsing query parameters.

Query parameters
Headers

Parsing header parameters.

Header parameters
Cookies

Parsing cookie parameters.

Cookie parameters
Path

Parsing path parameters.

Path parameters
Body

Parsing request body.

Request body
Files

Uploading files.

Uploading files

API Reference

class dmr.components.ComponentParser[source]

Base abstract provider for request components.

conditional_types(model: Any, model_meta: tuple[Any, ...]) Mapping[str, Any][source]

Provide conditional parsing types based on content type.

Some components parser might define different input models based on the request’s content type.

This method must return a mapping of content_type to the model. If this component support this.

context_name: ClassVar[str]

All subtypes must provide a unique name that will be used to parse context.

We use a single context for all parsing, this component will live under a dict field with this name.

abstractmethod get_schema(model: Any, model_meta: tuple[Any, ...], metadata: EndpointMetadata, serializer: type[BaseSerializer], context: OpenAPIContext) list[Parameter | Reference] | RequestBody[source]

Generate OpenAPI spec for component.

abstractmethod provide_context_data(endpoint: Endpoint, controller: Controller[BaseSerializer], *, field_model: Any) Any | tuple[Any, ...][source]

Return unstructured raw values for serializer.from_python().

It must return the same number of elements that has type vars. Basically, each type var is a model. Each element in a tuple is the corresponding data for that model.

When this method returns not a tuple and there’s only one type variable, it also works.

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

Return a list of extra responses that this component produces.

For example, when parsing something, we always have an option to fail a parsing, if some request does not fit our model.

validate(controller_cls: type[Controller[BaseSerializer]], metadata: EndpointMetadata) None[source]

Validates that the component is correctly defined.

By default does nothing. Runs in import time.