Integrations

Big list of Django integrations: https://github.com/wsvincent/awesome-django

Warning

In the future - some integrations from this list might be included into the core of django-modern-rest package. Or ship as plugins.

If you are interested in something: open an issue.

CSRF

Django supports Cross Site Request Forgery protection.

By default we exempt all controllers from CSRF checks, unless:

  1. csrf_exempt is set to False for a specific controller

  2. Endpoints protected by DjangoSessionSyncAuth or DjangoSessionAsyncAuth will require CSRF as well. Because using Django sessions without CSRF is not secure

Bring your own DI

We don’t have any opinions about any DI that you can potentially use. Because django-modern-rest is compatible with any of the existing ones.

Use any DI that you already have or want to use with django.

Try any of these officially recommended tools:

Or any other one that suits your needs :)

Typing

Django does not have type annotations, by default, so mypy won’t type check Django apps by default. But, when django-stubs is installed, type checking starts to shine.

So, when you use mypy, you will need to install django-stubs together with django-modern-rest to have the best type checking experience.

This package is included in pyright by default. No actions are required.

We check django-modern-rest code with mypy and pyright strict modes in CI, so be sure to have the best typing possible.

See our project template to learn how typing works, how mypy is configured, how django-stubs is used.

Pagination

Limit Offset pagination

We support built-in django.core.paginator.Paginator.

To do so, we only provide metadata for the default pagination:

Run result

$ curl http://127.0.0.1:8000/api/users/ -X GET
{"count":3,"num_pages":2,"per_page":2,"page":{"number":1,"object_list":[{"email":"one@example.com"},{"email":"two@example.com"}]}}

$ curl 'http://127.0.0.1:8000/api/users/?page=2' -X GET
{"count":3,"num_pages":2,"per_page":2,"page":{"number":2,"object_list":[{"email":"three@example.com"}]}}

OpenAPI Schema

Preview openapi.json
{
  "components": {
    "schemas": {
      "ErrorDetail": {
        "description": "Base schema for error details description.",
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "title": "Loc",
            "type": "array"
          },
          "msg": {
            "title": "Msg",
            "type": "string"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "msg"
        ],
        "title": "ErrorDetail",
        "type": "object"
      },
      "ErrorModel": {
        "description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorModel",
        "type": "object"
      },
      "Page__User_": {
        "description": "Default page model for serialization.\n\nCan be used when using pagination with ``django-modern-rest``.",
        "properties": {
          "number": {
            "title": "Number",
            "type": "integer"
          },
          "object_list": {
            "items": {
              "$ref": "#/components/schemas/_User"
            },
            "title": "Object List",
            "type": "array"
          }
        },
        "required": [
          "number",
          "object_list"
        ],
        "title": "Page",
        "type": "object"
      },
      "Paginated": {
        "description": "Helper type to serialize the default ``Paginator`` object.\n\nDjango already ships a pagination system, we don't want to replicate it.\nSo, we only provide metadata.\nSee :class:`django.core.paginator.Paginator` for the exact API.",
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "num_pages": {
            "title": "Num Pages",
            "type": "integer"
          },
          "page": {
            "$ref": "#/components/schemas/Page__User_"
          },
          "per_page": {
            "title": "Per Page",
            "type": "integer"
          }
        },
        "required": [
          "count",
          "num_pages",
          "per_page",
          "page"
        ],
        "title": "Paginated",
        "type": "object"
      },
      "_User": {
        "properties": {
          "email": {
            "title": "Email",
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "title": "_User",
        "type": "object"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/userscontroller/": {
      "get": {
        "deprecated": false,
        "operationId": "getUserscontrollerApiUserscontroller",
        "parameters": [
          {
            "deprecated": false,
            "in": "query",
            "name": "page_size",
            "schema": {
              "title": "Page Size",
              "type": "integer"
            }
          },
          {
            "deprecated": false,
            "in": "query",
            "name": "page",
            "schema": {
              "title": "Page",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Paginated"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when request components cannot be parsed"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

If you are using a different pagination system, you can define your own metadata / models and use them with our framework.

Cursor pagination

We also support any other pagination library.

Like django-cursor-pagination or even your custom implementation.

Any Django-compatible tool should work out of the box.

Interface

class dmr.pagination.Paginated(*, count: int, num_pages: int, per_page: int, page: Page[_ModelT])[source]

Helper type to serialize the default Paginator object.

Django already ships a pagination system, we don’t want to replicate it. So, we only provide metadata. See django.core.paginator.Paginator for the exact API.

class dmr.pagination.Page(*, number: int, object_list: Sequence[_ModelT])[source]

Default page model for serialization.

Can be used when using pagination with django-modern-rest.

Filters

No special integration with django-filter is required.

Everything just works:

Run result

$ curl 'http://127.0.0.1:8000/api/users/?is_active=1' -X GET
[{"username":"test_user","email":"test@example.com","is_active":true}]

OpenAPI Schema

Preview openapi.json
{
  "components": {
    "schemas": {
      "ErrorDetail": {
        "description": "Base schema for error details description.",
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "title": "Loc",
            "type": "array"
          },
          "msg": {
            "title": "Msg",
            "type": "string"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "msg"
        ],
        "title": "ErrorDetail",
        "type": "object"
      },
      "ErrorModel": {
        "description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorModel",
        "type": "object"
      },
      "UserModel": {
        "properties": {
          "email": {
            "title": "Email",
            "type": "string"
          },
          "is_active": {
            "title": "Is Active",
            "type": "boolean"
          },
          "username": {
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "username",
          "email",
          "is_active"
        ],
        "title": "UserModel",
        "type": "object"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/userscontroller/": {
      "get": {
        "deprecated": false,
        "operationId": "getUserscontrollerApiUserscontroller",
        "parameters": [
          {
            "deprecated": false,
            "in": "query",
            "name": "is_active",
            "required": true,
            "schema": {
              "title": "Is Active",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/UserModel"
                  },
                  "type": "array"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when request components cannot be parsed"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

Health Checks

We recommend using django-health-check for monitoring your application’s health.

No special integration is required — the package works out-of-the-box with django-modern-rest. Simply install it, include its URLs in your main urlconf, and add the desired check apps to INSTALLED_APPS.

For advanced configuration, please refer to the django-health-check documentation.

CORS Headers

No special integration with django-cors-headers is required.

Everything just works.

Content Security Policy (CSP)

No special integration with django-csp is required.

Everything just works, but there is one important nuance: django-modern-rest itself only controls Django templates and local initialization files. If you use OpenAPI UI renderers, final CSP compatibility still depends on the upstream frontend bundle you choose.

The OpenAPI UI templates shipped by django-modern-rest avoid inline <script> blocks and pass schema data via Django’s django.utils.html.json_script(), so DMR’s own templates work well with stricter CSP setups.

Known caveats:

  • Some upstream OpenAPI bundles inject styles at runtime, so a very strict policy can still break the page.

  • When CSP is a hard requirement, start with local bundled assets and test the exact renderer and version you plan to deploy.

Example django-csp setup can be found in wemake-django-template.

If you use OpenAPI UIs, see OpenAPI for renderer-specific guidance.

Conditional requests (ETag)

Django has built-in support for conditional request processing (If-None-Match, If-Modified-Since, 304 Not Modified):

With django-modern-rest you can integrate it via wrap_middleware() and django.views.decorators.http.condition().

Run result

$ curl http://127.0.0.1:8000/api/example/1/ -D - -X GET
HTTP/1.1 200 OK
date: Tue, 14 Apr 2026 17:08:41 GMT
server: uvicorn
Content-Type: application/json
ETag: "user-1-2026-03-23T12:30:00+00:00"
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 80
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

{"message":"Fresh content for user #1","updated_at":"2026-03-23T12:30:00+00:00"}

$ curl http://127.0.0.1:8000/api/example/1/ -D - -X GET -H 'If-None-Match: "user-1-2026-03-23T12:30:00+00:00"'
HTTP/1.1 304 Not Modified
date: Tue, 14 Apr 2026 17:08:41 GMT
server: uvicorn
ETag: "user-1-2026-03-23T12:30:00+00:00"
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 0
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin


$ curl http://127.0.0.1:8000/api/example/2/ -D - -X GET
HTTP/1.1 200 OK
date: Tue, 14 Apr 2026 17:08:41 GMT
server: uvicorn
Content-Type: application/json
ETag: "user-2-2026-03-24T09:15:00+00:00"
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 80
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

{"message":"Fresh content for user #2","updated_at":"2026-03-24T09:15:00+00:00"}

OpenAPI Schema

Preview openapi.json
{
  "components": {
    "schemas": {
      "ErrorDetail": {
        "description": "Base schema for error details description.",
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "title": "Loc",
            "type": "array"
          },
          "msg": {
            "title": "Msg",
            "type": "string"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "msg"
        ],
        "title": "ErrorDetail",
        "type": "object"
      },
      "ErrorModel": {
        "description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorModel",
        "type": "object"
      },
      "_ResponseModel": {
        "properties": {
          "message": {
            "title": "Message",
            "type": "string"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "message",
          "updated_at"
        ],
        "title": "_ResponseModel",
        "type": "object"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/example/{user_id}/": {
      "get": {
        "deprecated": false,
        "operationId": "getConditionaletagcontrollerApiExampleUserId",
        "parameters": [
          {
            "deprecated": false,
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/_ResponseModel"
                }
              }
            },
            "description": "OK"
          },
          "304": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "null"
                }
              }
            },
            "description": "Not Modified",
            "headers": {
              "ETag": {
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when request components cannot be parsed"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when path parameters do not match"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

HTMX

Works with django-htmx out of the box.