Configuration

We use DMR_SETTINGS dictionary object to store all the configuration. All keys are typed with Settings enum keys which can be used to both set and get settings.

Note

Remember, that django-modern-rest settings are cached after the first access. If you need to modify settings dynamically in runtime use clear_settings_cache(). You can modify the size of cache with adjusting DMR_MAX_CACHE_SIZE value.

Here are all keys and values that can be set. As usual, all settings go to settings.py file in your Django project.

See also

We also validate defined settings in import time. See Settings validation for more details.

Settings

Class that can be used to properly type settings in user’s code:

class dmr.settings.SettingsDict[source]

Settings type that can be used for typing.

Class with all possible setting keys as enum:

final class dmr.settings.Settings(*values)[source]

Bases: StrEnum

Keys for all settings.

To get settings use resolve_setting() function together with Settings keys:

>>> from dmr.settings import Settings, resolve_setting

>>> resolve_setting(Settings.responses)
[]

To set settings use:

>>> DMR_SETTINGS = {Settings.responses: []}

Content negotiation

Note

It is recommended to always install msgspec with 'django-modern-rest[msgspec]' extra for better performance.

dmr.settings.Settings.parsers

Default: dmr.parsers.JsonParser or dmr.plugins.msgspec.MsgspecJsonParser if installed.

A list of instances of subtypes of Parser to serialize data from the requested text format, like json or xml, into python object.

Custom configuration example, let’s say you want to always use ujson:

settings.py
>>> from dmr.parsers import JsonParser
>>> DMR_SETTINGS = {Settings.parsers: [JsonParser()]}
dmr.settings.Settings.renderers

Default: dmr.renderers.JsonRenderer or dmr.plugins.msgspec.MsgspecJsonRenderer if installed.

A list of instances of subtypes of Renderer to serialize python objects to the requested text format, like json or xml.

Custom configuration example, let’s say you want to always use ujson:

settings.py
>>> from dmr.renderers import JsonRenderer
>>> DMR_SETTINGS = {Settings.renderers: [JsonRenderer()]}
dmr.settings.Settings.validate_negotiation

Default: None

Should we validate content negotiation? Meaning: django-modern-rest finds which parser and which renderer to use based on Content-Type and Accept headers. However, by mistake people can return responses with wrong Content-Type if they construct responses manually. See Content negotiation for more info.

Defaults to the value set in validate_responses for convenience if this value is None.

To disable the content negotiation validation globally, use:

settings.py
>>> DMR_SETTINGS = {
...     Settings.validate_negotiation: False,
... }

Response handling

dmr.settings.Settings.responses

Default: []

The list of global ResponseSpec object that will be added to all endpoints’ metadata as a possible response schema.

Use it to set global responses’ status codes like 500:

settings.py
>>> from http import HTTPStatus
>>> from typing_extensions import TypedDict
>>> from dmr import ResponseSpec

>>> class Error(TypedDict):
...     detail: str

>>> # If our API can always return a 500 response with `{"detail": str}`
>>> # error message:
>>> DMR_SETTINGS = {
...     Settings.responses: [
...         ResponseSpec(
...             Error,
...             status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
...         ),
...     ],
... }
dmr.settings.Settings.validate_responses

Default: True

When some endpoint returns any data, we by default validate that this data matches the endpoint schema.

So, this code will produce not only static typing error, but also a runtime error:

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

>>> class MyController(Controller[PydanticSerializer]):
...     def get(self) -> list[str]:
...         return [1, 2]  # <- both static typing and runtime error

But, there’s a runtime cost to this. It is recommended to switch this validation off for production:

settings.py
>>> DMR_SETTINGS = {Settings.validate_responses: False}

Note

You can also switch off this validation per-controller with validate_responses and per-endpoint with validate_responses argument to modify() and validate().

dmr.settings.Settings.semantic_responses

Default: True

When True, parsers, renderers, and authentication classes automatically inject their semantic response specs (e.g. 422 Unprocessable Entity, 406 Not Acceptable) into every endpoint’s metadata. These responses are then visible in the generated OpenAPI spec.

Set to False to disable this auto-injection globally. User-defined responses (via @modify, @validate, controller responses, or Settings.responses) are not affected by this flag. Runtime response validation still works as configured by Settings.validate_responses.

settings.py
>>> DMR_SETTINGS = {Settings.semantic_responses: False}
dmr.settings.Settings.exclude_semantic_responses

Default: frozenset()

Pass any status code, that you wanna exclude from semantic responses.

settings.py
>>> from http import HTTPStatus

>> DMR_SETTINGS = {
...    Settings.exclude_semantic_responses: {
...        HTTPStatus.CONFLICT,
...    },
... }

When this value is set to None at any level, this means that the value is reset. For example, setting exclude_semantic_responses=None on endpoint level will cancel all controller and settings level values and enable all responses back again.

Error handling

dmr.settings.Settings.global_error_handler

Default: 'dmr.errors.global_error_handler'

Globally handle all errors in the application. You can use real object or string path for the object to be imported. Here’s our error handling hierarchy:

  1. Per-endpoint with handle_error() and handle_async_error()

  2. Per-controller with handle_error() or handle_async_error()

  3. If nothing helped, 'global_error_handler' is called

See global_error_handler() for the callback type.

settings.py
>>> DMR_SETTINGS = {Settings.global_error_handler: 'path.to.your.handler'}

Authentication

dmr.settings.Settings.auth

Default: []

Configure authentication rules for the whole API.

To enable auth for all endpoints you can use:

settings.py
>>> from dmr.security.django_session import DjangoSessionSyncAuth

>>> DMR_SETTINGS = {
...     Settings.auth: [
...         DjangoSessionSyncAuth(),
...     ],
... }

All auth types must be importable in settings.

Throttling

dmr.settings.Settings.throttling

Default: []

Configure throttling rules for the whole API.

To enable throttling for all endpoints you can use:

settings.py
>>> from dmr.throttling import SyncThrottle, Rate

>>> DMR_SETTINGS = {
...     Settings.throttling: [
...         SyncThrottle(10, Rate.second),
...     ],
... }

All throttle types must be importable in settings.

HTTP Spec validation

dmr.settings.Settings.no_validate_http_spec

Default: frozenset()

A set of unique HttpSpec codes to be globally disabled.

We don’t recommend disabling any of these checks globally.

settings.py
>>> from dmr.settings import HttpSpec

>>> DMR_SETTINGS = {
...     Settings.no_validate_http_spec: {
...         HttpSpec.empty_request_body,
...     },
... }

When this value is set to None at any level, this means that the value is reset. For example, setting no_validate_http_spec=None on endpoint level will cancel all controller and settings level values and enable all validation back again.

final class dmr.settings.HttpSpec(*values)[source]

Bases: StrEnum

Keys for our HTTP spec validation.

All rules can be disabled per endpoint and per controller. You can disable any of the validation rules we have here globally by:

>>> DMR_SETTINGS = {
...     Settings.no_validate_http_spec: {
...         HttpSpec.empty_response_body,
...     },
... }
empty_request_body

Disables validation that methods like GET and HEAD can’t have request bodies.

empty_response_body

Disables validation that some status codes like 204 must not have response bodies.

Streaming

dmr.settings.Settings.validate_events

Default: None

Should we validate the events in all streams? Defaults to the value set in validate_responses for convenience if this value is None.

To disable the event validation globally, use:

settings.py
>>> DMR_SETTINGS = {
...     Settings.validate_events: False,
... }

OpenAPI

dmr.settings.Settings.openapi_config

Default: OpenAPIConfig(title='Django Modern Rest', version='0.1.0')

Metadata to be used in the OpenAPI schema.

See OpenAPIConfig for the available fields and their description. It can also be used to change the default OpenAPI spec version.

settings.py
>>> from dmr.openapi.config import OpenAPIConfig

>>> DMR_SETTINGS = {
...     Settings.openapi_config: OpenAPIConfig(
...         title='My Amazing App',
...         version='13.22.3',
...         openapi_version='3.2.0',
...     ),
... }
dmr.settings.Settings.openapi_examples_seed

Default: None

Random seed to use when generating missing examples in the OpenAPI spec.

If set to None, no examples are generated. Existing examples won’t be overridden.

It only works if 'django-modern-rest[openapi]' extra is installed. If polyfactory package is missing, no examples are generated.

settings.py
>>> from dmr.settings import HttpSpec
>>> from dmr.openapi.config import OpenAPIConfig

>>> DMR_SETTINGS = {
...     Settings.openapi_examples_seed: 10,
... }
dmr.settings.Settings.openapi_static_cdn

Default: {}

Optional mapping to switch OpenAPI renderers to CDN resources. Only renderers explicitly listed in this mapping will use CDN, the rest will use local bundled static files served by Django.

Supported keys are:

  • swagger: base URL to swagger-ui-dist (without file name)

  • redoc: full URL to redoc.standalone.js

  • scalar: full URL to @scalar/api-reference standalone bundle

  • stoplight: base URL to @stoplight/elements (without file name)

You can also modify the exact versions that we use for each tool this way.

settings.py
>>> DMR_SETTINGS = {
...     Settings.openapi_static_cdn: {
...         'swagger': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.32.1',
...         'redoc': 'https://cdn.redoc.ly/redoc/2.5.2/bundles/redoc.standalone.js',
...         'scalar': 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.49.2/dist/browser/standalone.js',
...         'stoplight': 'https://unpkg.com/@stoplight/elements@9.0.16',
...     },
... }

Hacks

dmr.settings.Settings.django_treat_as_post

Default: frozenset({'PUT', 'PATCH'})

By default Django only populates django.http.HttpRequest.POST and django.http.HttpRequest.FILES for POST requests.

Some parsers like MultiPartParser require .POST and .FILES to be set to work with Body and FileMetadata.

However, we build REST APIs where more methods are in use, not just POST. So, we use this setting to populate .POST and .FILES when parser is a subtype of either SupportsFileParsing or SupportsDjangoDefaultParsing and we work with components that require request body.

Note

This is a really advanced setting. If you are not creating your own components or your own parsers - do not change it.

Environment variables

DMR_MAX_CACHE_SIZE

Default: 256

We use functools.lru_cache() in many places internally. For example:

  • To create json encoders and decoders only once

  • To create type validation objects in BaseEndpointOptimizer

You can control the size / memory usage with this setting.

Increase if you have a lot of different return types.

DMR_USE_COMPILED

Default: 1

We compile some modules to C-extensions with mypyc compilation. If you want to disable the extensions and fallback to pure Python implementation, set this variable to 0.

It is only recommended for debugging. It should be set to 1 in production for maximum speed.

API Reference

dmr.settings.resolve_setting(setting_name: Settings, *, import_string: bool = False) Any[source]

Resolves setting by setting_name.

The result is cached using @lru_cache for performance. When testing with custom settings, you must call clear_settings_cache() before and after modifying Django settings to ensure the cache is invalidated properly.

dmr.settings.clear_settings_cache() None[source]

Clears settings cache for all functions in this module.

Useful for tests, when you modify the global settings object.