Request body#
The body can be anything: JSON, XML,
application/x-www-form-urlencoded,
or multipart/form-data.
It depends on the Parser
that is being used for the endpoint.
Note
The parsed Body parameter must be named parsed_body.
Parsing JSON#
Here’s how you can parse Body with a model:
We support msgspec.Struct
via MsgspecSerializer.
1import msgspec
2
3from dmr import Body, Controller
4from dmr.plugins.msgspec import MsgspecSerializer
5
6
7class _User(msgspec.Struct):
8 username: str
9 age: int
10
11
12class UserController(Controller[MsgspecSerializer]):
13 def put(self, parsed_body: Body[_User]) -> _User:
14 return parsed_body
15
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d '{"username": "sobolevn", "age": 27}' -H 'Content-Type: application/json'
{"username":"sobolevn","age":27}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d '{"username": "sobolevn"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:37 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 100
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Object missing required field `age` - at `$.parsed_body`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_User": {
"properties": {
"age": {
"type": "integer"
},
"username": {
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
We support pydantic.BaseModel
via PydanticSerializer.
1import pydantic
2
3from dmr import Body, Controller
4from dmr.plugins.pydantic import PydanticFastSerializer
5
6
7class _User(pydantic.BaseModel):
8 username: str
9 age: int
10
11
12class UserController(Controller[PydanticFastSerializer]):
13 def put(self, parsed_body: Body[_User]) -> _User:
14 return parsed_body
15
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d '{"username": "sobolevn", "age": 27}' -H 'Content-Type: application/json'
{"username":"sobolevn","age":27}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d '{"username": "sobolevn"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:38 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 86
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Field required","loc":["parsed_body","age"],"type":"value_error"}]}
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"
},
"_User": {
"properties": {
"age": {
"title": "Age",
"type": "integer"
},
"username": {
"title": "Username",
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
We support attrs.define()
via MsgspecSerializer.
1import attrs
2
3from dmr import Body, Controller
4from dmr.plugins.msgspec import MsgspecSerializer
5
6
7@attrs.define
8class _User:
9 username: str
10 age: int
11
12
13class UserController(Controller[MsgspecSerializer]):
14 def put(self, parsed_body: Body[_User]) -> _User:
15 return parsed_body
16
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d '{"username": "sobolevn", "age": 27}' -H 'Content-Type: application/json'
{"age":27,"username":"sobolevn"}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d '{"username": "sobolevn"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:39 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 100
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Object missing required field `age` - at `$.parsed_body`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_User": {
"properties": {
"age": {
"type": "integer"
},
"username": {
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
We support dataclasses.dataclass() via both
MsgspecSerializer
and PydanticSerializer.
1import dataclasses
2
3from dmr import Body, Controller
4from dmr.plugins.msgspec import MsgspecSerializer
5
6
7@dataclasses.dataclass
8class _User:
9 username: str
10 age: int
11
12
13class UserController(Controller[MsgspecSerializer]):
14 def put(self, parsed_body: Body[_User]) -> _User:
15 return parsed_body
16
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d '{"username": "sobolevn", "age": 27}' -H 'Content-Type: application/json'
{"username":"sobolevn","age":27}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d '{"username": "sobolevn"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:40 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 100
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Object missing required field `age` - at `$.parsed_body`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_User": {
"properties": {
"age": {
"type": "integer"
},
"username": {
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
We support typing.TypedDict via both
MsgspecSerializer
and PydanticSerializer.
1from typing_extensions import TypedDict
2
3from dmr import Body, Controller
4from dmr.plugins.msgspec import MsgspecSerializer
5
6
7class _User(TypedDict):
8 username: str
9 age: int
10
11
12class UserController(Controller[MsgspecSerializer]):
13 def put(self, parsed_body: Body[_User]) -> _User:
14 return parsed_body
15
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d '{"username": "sobolevn", "age": 27}' -H 'Content-Type: application/json'
{"username":"sobolevn","age":27}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d '{"username": "sobolevn"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:41 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 100
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Object missing required field `age` - at `$.parsed_body`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_User": {
"properties": {
"age": {
"type": "integer"
},
"username": {
"type": "string"
}
},
"required": [
"age",
"username"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
What happens in this example?
We define a
Bodymodel usingmsgspec.Struct,pydantic.BaseModel,attrs.define(),typing.TypedDict, ordataclasses.dataclass(). Basically, the model definition is only limited by theBaseSerializersupportNext, we use the
Bodycomponent, provide the model as a type parameter, and subclass it when defining theControllertypeThen we use
self.parsed_bodywhich will have the correct model type
Parsing MsgPack#
Note
This feature requires 'django-modern-rest[msgpack]' to be installed.
MsgPack is a binary, compact and really fast format for modern APIs. Docs: https://msgpack.org
Bodies can be parsed using different dmr.parsers.Parser types.
See our Content negotiation guide on more information
about content negotiation.
Here’s how msgpack will represent {"username": "example", "age": 22}
(since it is a binary format, it will show some random unicode symbols:
The only visible difference from parsing JSON is specifying a different
parsers instance.
1import msgspec
2
3from dmr import Body, Controller
4from dmr.plugins.msgspec import MsgpackParser, MsgspecSerializer
5
6
7class _User(msgspec.Struct):
8 username: str
9 age: int
10
11
12class UserController(Controller[MsgspecSerializer]):
13 parsers = (MsgpackParser(),)
14
15 def put(self, parsed_body: Body[_User]) -> _User:
16 return parsed_body
17
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT --data-binary @examples/components/body.msgpack -H 'Content-Type: application/msgpack'
{"username":"example","age":22}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT --data-binary @examples/components/body_wrong.msgpack -H 'Content-Type: application/msgpack'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:42 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 100
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Object missing required field `age` - at `$.parsed_body`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_User": {
"properties": {
"age": {
"type": "integer"
},
"username": {
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/msgpack": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
Optional body#
Request body can have a default value, in this case it is optional:
1import msgspec
2
3from dmr import Body, Controller
4from dmr.plugins.msgspec import MsgspecSerializer
5
6
7class ProductFilters(msgspec.Struct):
8 category: str
9
10
11class ProductController(Controller[MsgspecSerializer]):
12 def post(
13 self,
14 parsed_body: Body[ProductFilters | None] = None,
15 ) -> str:
16 return 'all' if parsed_body is None else parsed_body.category
17
Run result
$ curl http://127.0.0.1:8000/api/products/ -X POST
"all"
$ curl http://127.0.0.1:8000/api/products/ -X POST -d '{"category": "cars"}' -H 'Content-Type: application/json'
"cars"
$ curl http://127.0.0.1:8000/api/products/ -D - -X POST -d '{"other": "cars"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:43 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 105
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Object missing required field `category` - at `$.parsed_body`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"ProductFilters": {
"properties": {
"category": {
"type": "string"
}
},
"required": [
"category"
],
"title": "ProductFilters",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/productcontroller/": {
"post": {
"operationId": "postProductControllerApiProductcontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/ProductFilters"
},
{
"type": "null"
}
]
}
}
}
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
},
"description": "Created"
},
"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"
}
}
}
}
}
}
1import pydantic
2
3from dmr import Body, Controller
4from dmr.plugins.pydantic import PydanticSerializer
5
6
7class ProductFilters(pydantic.BaseModel):
8 category: str
9
10
11class ProductController(Controller[PydanticSerializer]):
12 def post(
13 self,
14 parsed_body: Body[ProductFilters | None] = None,
15 ) -> str:
16 return 'all' if parsed_body is None else parsed_body.category
17
Run result
$ curl http://127.0.0.1:8000/api/products/ -X POST
"all"
$ curl http://127.0.0.1:8000/api/products/ -X POST -d '{"category": "cars"}' -H 'Content-Type: application/json'
"cars"
$ curl http://127.0.0.1:8000/api/products/ -D - -X POST -d '{"other": "cars"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:44 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 91
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Field required","loc":["parsed_body","category"],"type":"value_error"}]}
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"
},
"ProductFilters": {
"properties": {
"category": {
"title": "Category",
"type": "string"
}
},
"required": [
"category"
],
"title": "ProductFilters",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/productcontroller/": {
"post": {
"operationId": "postProductControllerApiProductcontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/ProductFilters"
},
{
"type": "null"
}
]
}
}
}
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
},
"description": "Created"
},
"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"
}
}
}
}
}
}
Requests without a body get parsed_body=None,
such bodies are documented with required: false in the OpenAPI schema.
Requests with a body are always validated, even when there’s a default.
See Default values to learn more.
Fast body parsing with msgspec#
Added in version 0.16.0.
Tip
BodyMsgspec is generally x1.6 faster than Body.
By default, Body is parsed in two steps:
The
Parserdecodes the raw request bytes into simple python objects, likedictandlist, using no exact shapeThen, all components of the request (body, headers, query, etc) are validated together in a single call with the model that the
BaseSerializerbuilds for the whole request, see Customizing the serializer context
This is flexible, but the intermediate python objects cost time.
But, msgspec can decode bytes directly into a model,
when it knows the shape in advance. It gives a lot of speed: for example,
msgspec skips unknown fields and does not parse them from the body payload.
It is a drop-in replacement for Body when using
MsgspecSerializer:
1import msgspec
2
3from dmr import Controller
4from dmr.plugins.msgspec import BodyMsgspec, MsgspecSerializer
5
6
7class _User(msgspec.Struct, gc=False):
8 username: str
9 age: int
10
11
12class UserController(Controller[MsgspecSerializer]):
13 def put(self, parsed_body: BodyMsgspec[_User]) -> _User:
14 return parsed_body
15
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d '{"username": "sobolevn", "age": 27}' -H 'Content-Type: application/json'
{"username":"sobolevn","age":27}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d '{"username": "sobolevn", "age": "not-a-number"}' -H 'Content-Type: application/json'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:45 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 82
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Expected `int`, got `str` - at `$.age`","type":"value_error"}]}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"type": "array"
},
"msg": {
"type": "string"
},
"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"
},
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
},
"_User": {
"properties": {
"age": {
"type": "integer"
},
"username": {
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
What is different from Body?
Only
MsgspecSerializeris supported, other serializers raiseEndpointMetadataErrorduring the import time.pydanticcannot benefit from this: its models can’t be decoded bymsgspec, and validating them from within the decoder is slower than the regularBodyin all measured casesThe body is validated on its own, before all other components are validated together. When the body is invalid, other components are not validated at all: the error response only contains body errors, even if
parsed_headersorparsed_queryare also invalidError locations change: the body is validated as the root object, not as a part of the whole request, so the
parsed_bodyprefix is gone.Bodyreports$.parsed_body.age,BodyMsgspecreports$.agefor the same error. Top level errors have no location at allInvalid bytes, like malformed
json, are still reported as parsing errors, exactly like forBodyStrictness of
msgspecdecoding is controlled bystrictandstrictattributes, which are lax by default, just like the regular request validation.strict_validationhas no effect on fast bodies
Tip
BodyMsgspec works best if there are no other components.
Everything else works the same: defaults,
conditional types, OpenAPI schema generation,
and response validation.
The OpenAPI schema is always the same for Body and BodyMsgspec.
Note
Only parsers that decode into models really support BodyMsgspec:
MsgspecJsonParser
and MsgpackParser.
Custom parsers can also learn to respect the model, but it needs a support from the core library’s serializer side.
Customizing the OpenAPI metadata for Body#
Parsing forms#
Note
We don’t recommend using forms. If you can avoid using this feature and switch to JSON, you totally should.
Forms are only needed for compatibility with older APIs, strange libraries, or existing workflows.
Here’s an example how one can send application/x-www-form-urlencoded
form data to an API endpoint with the help
of FormUrlEncodedParser:
1import pydantic
2
3from dmr import Body, Controller
4from dmr.parsers import FormUrlEncodedParser
5from dmr.plugins.pydantic import PydanticSerializer
6
7
8class _User(pydantic.BaseModel):
9 username: str
10 age: int
11
12
13class UserController(Controller[PydanticSerializer]):
14 parsers = (FormUrlEncodedParser(),)
15
16 def put(self, parsed_body: Body[_User]) -> _User:
17 return parsed_body
18
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -d 'username=sobolevn&age=27' -H 'Content-Type: application/x-www-form-urlencoded'
{"username":"sobolevn","age":27}
$ curl http://127.0.0.1:8000/api/users/ -D - -X PUT -d 'username=sobolevn&age=wrong' -H 'Content-Type: application/x-www-form-urlencoded'
HTTP/1.1 400 Bad Request
date: Mon, 05 Oct 2026 11:16:46 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 141
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
{"detail":[{"msg":"Input should be a valid integer, unable to parse string as an integer","loc":["parsed_body","age"],"type":"value_error"}]}
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"
},
"_User": {
"properties": {
"age": {
"title": "Age",
"type": "integer"
},
"username": {
"title": "Username",
"type": "string"
}
},
"required": [
"username",
"age"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
Tip
If you are using Django 6.1+ and have a lot of form requests,
you can swap the django.http.HttpRequest.multipart_parser_class
attribute of request objects for faster parsing. We recommend using
the multipart package for that.
It works twice as fast.
Forcing lists and casting nulls in forms#
Warning
All of the features below only work for
application/x-www-form-urlencoded and multipart/form-data
parsers. JSON and other “modern” formats are not affected.
Django’s form parsing algorithm is 20+ years old at the moment of writing this doc.
There are some known quirks to it.
Forcing lists#
Django uses django.utils.datastructures.MultiValueDict
to store body data when parsing forms. Due to its API,
it does not give list objects back easily.
So, when we need a list for a field, we need to force it like this:
1from typing import ClassVar
2
3import pydantic
4
5from dmr import Body, Controller
6from dmr.parsers import MultiPartParser
7from dmr.plugins.pydantic import PydanticSerializer
8
9
10class _User(pydantic.BaseModel):
11 __dmr_force_list__: ClassVar[frozenset[str]] = frozenset(('tags',))
12
13 username: str
14 tags: list[str]
15
16
17class UserController(Controller[PydanticSerializer]):
18 parsers = (MultiPartParser(),)
19
20 def put(self, parsed_body: Body[_User]) -> _User:
21 return parsed_body
22
Run result
$ curl http://127.0.0.1:8000/api/users/ -X PUT -F username=sobolevn -F tags=python -F tags=django -H 'Content-Type: multipart/form-data'
{"username":"sobolevn","tags":["python","django"]}
$ curl http://127.0.0.1:8000/api/users/ -X PUT -F username=sobolevn -F tags=single -H 'Content-Type: multipart/form-data'
{"username":"sobolevn","tags":["single"]}
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"
},
"_User": {
"properties": {
"tags": {
"items": {
"type": "string"
},
"title": "Tags",
"type": "array"
},
"username": {
"title": "Username",
"type": "string"
}
},
"required": [
"username",
"tags"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"put": {
"operationId": "putUserControllerApiUsercontroller",
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"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"
}
}
}
}
}
}
Split commas#
Another problem that might happen is that some field might
look like {'foo': 'bar,baz'}, not {'foo': ['bar', 'baz']}.
To solve this, one can use a different magic attribute:
1from typing import ClassVar
2
3import pydantic
4
5from dmr import Body, Controller
6from dmr.parsers import MultiPartParser
7from dmr.plugins.pydantic import PydanticSerializer
8
9
10class _User(pydantic.BaseModel):
11 __dmr_split_commas__: ClassVar[frozenset[str]] = frozenset(('tags',))
12
13 username: str
14 tags: list[str]
15
16
17class UserController(Controller[PydanticSerializer]):
18 parsers = (MultiPartParser(),)
19
20 def post(self, parsed_body: Body[_User]) -> _User:
21 return parsed_body
22
Run result
$ curl http://127.0.0.1:8000/api/users/ -X POST -F username=sobolevn -F tags=python,django -H 'Content-Type: multipart/form-data'
{"username":"sobolevn","tags":["python","django"]}
$ curl http://127.0.0.1:8000/api/users/ -X POST -F username=sobolevn -F tags=single -H 'Content-Type: multipart/form-data'
{"username":"sobolevn","tags":["single"]}
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"
},
"_User": {
"properties": {
"tags": {
"items": {
"type": "string"
},
"title": "Tags",
"type": "array"
},
"username": {
"title": "Username",
"type": "string"
}
},
"required": [
"username",
"tags"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"post": {
"operationId": "postUserControllerApiUsercontroller",
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"description": "Created"
},
"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"
}
}
}
}
}
}
Warning
We split all data by ','. If your data contains ',' as a regular
value, it might be corrupted.
Be careful when you use this with fields which do not contain ',',
like list of ints, UUIDs, or slugs.
Casting nulls#
It is hard to pass None as a value in a form.
To solve the need for None, many places offer to pass 'null' as a string.
We can cast 'null' back to None if __dmr_cast_null__ is specified.
1from typing import ClassVar
2
3import pydantic
4
5from dmr import Body, Controller
6from dmr.parsers import MultiPartParser
7from dmr.plugins.pydantic import PydanticSerializer
8
9
10class _User(pydantic.BaseModel):
11 __dmr_force_list__: ClassVar[frozenset[str]] = frozenset(('tags',))
12 __dmr_cast_null__: ClassVar[frozenset[str]] = frozenset((
13 'promo_id',
14 'tags',
15 ))
16
17 username: str
18 promo_id: int | None
19 tags: list[str | None]
20
21
22class UserController(Controller[PydanticSerializer]):
23 parsers = (MultiPartParser(),)
24
25 def post(self, parsed_body: Body[_User]) -> _User:
26 return parsed_body
27
Run result
$ curl http://127.0.0.1:8000/api/users/ -X POST -F username=sobolevn -F promo_id=1 -F tags=python -F tags=django -H 'Content-Type: multipart/form-data'
{"username":"sobolevn","promo_id":1,"tags":["python","django"]}
$ curl http://127.0.0.1:8000/api/users/ -X POST -F username=sobolevn -F promo_id=null -F tags=python -F tags=null -H 'Content-Type: multipart/form-data'
{"username":"sobolevn","promo_id":null,"tags":["python",null]}
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"
},
"_User": {
"properties": {
"promo_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Promo Id"
},
"tags": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"title": "Tags",
"type": "array"
},
"username": {
"title": "Username",
"type": "string"
}
},
"required": [
"username",
"promo_id",
"tags"
],
"title": "_User",
"type": "object"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/usercontroller/": {
"post": {
"operationId": "postUserControllerApiUsercontroller",
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/_User"
}
}
},
"description": "Created"
},
"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"
}
}
}
}
}
}
You can combine this feature with
both __dmr_split_commas__ and __dmr_force_list__ as well.
Conditional models#
django-modern-rest supports using different request models
for different request content types.
See Using different schemes for different content types to learn more.
API Reference#
- dmr.components.Body#
Annotated alias for parsing requests bodies.
alias of
Annotated[_BodyT, <dmr.components.BodyComponent object at 0x7170bff25900>]
- class dmr.components.BodyComponent(*, pass_model: bool = False)[source]#
Bases:
ComponentParserParses body of the request.
For example:
>>> import pydantic >>> from dmr import Body, Controller >>> from dmr.plugins.pydantic import PydanticSerializer >>> class UserCreateInput(pydantic.BaseModel): ... email: str ... age: int >>> class UserCreateController(Controller[PydanticSerializer]): ... def post(self, parsed_body: Body[UserCreateInput]) -> str: ... return parsed_body.email
Will parse a body like
{'email': 'user@example.org', 'age': 18}intoUserCreateInputmodel.Parameter for
Bodycomponent must be namedparsed_body.When working with parsers that support
dmr.parsers.SupportsDjangoDefaultParsinginterface, you can specify__dmr_split_commas__attribute: it must contain afrozensetof field aliases that will be split by','char.See Using different schemes for different content types to learn more about conditional bodies.
- Parameters:
pass_model – Whether or not to pass
modelto the serializer orAny. Plugins that support direct model parsing provide their own aliases, likeBodyMsgspec, and validate that their serializer is used to parse objects faster. See Fast body parsing with msgspec to learn more.
Changed in version 0.16.0: Added pass_model parameter.
- conditional_types(model: TypeForm[Any], model_meta: tuple[Any, ...]) Mapping[str, Any][source]#
Provide conditional parsing types based on content type.
Body model can be conditional based on a content_type. If
typing.Annotatedis passed together withdmr.negotiation.conditional_type()we treat the body as conditional. Otherwise, returns an empty dict.
- context_name: ClassVar[str] = 'parsed_body'#
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.
- get_schema(model: TypeForm[Any], model_meta: tuple[Any, ...], metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], context: OpenAPIContext) list[Parameter | Reference] | RequestBody[source]#
Generate OpenAPI spec for component.
Changed in version 0.16.0: serializer parameter was changed to be controller_cls.
- provide_context_data(endpoint: Endpoint, controller: Controller[BaseSerializer], *, field_model: TypeForm[Any], default: Any = EMPTY) 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.
default is the default value of the endpoint parameter, like
parsed_body: Body[Model | None] = None. When it is notEMPTYand the request has no data for this component, return default as-is.Changed in version 0.16.0: Added default parameter.