Returning files#

We support file and other binary responses.

Warning

Returning files via Python and Django in particular is very performance inefficient. It should not be used for anything serious.

Instead return files with S3-like systems or at least on a proxy-server level.

Returning file contents#

To do so, you indicate that you will return a file with dmr.files.FileResponseSpec and specify a file renderer. We provide dmr.renderers.FileRenderer for this case.

By default, FileResponseSpec() describes an inline file response. It matches Django’s FileResponse default behavior:

Run result

$ curl http://127.0.0.1:8000/api/file/ -D - -X GET
HTTP/1.1 200 OK
date: Mon, 05 Oct 2026 11:18:51 GMT
server: uvicorn
Content-Type: text/plain
Content-Length: 5
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

Hello

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"
      }
    }
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/inlinefilecontroller/": {
      "get": {
        "operationId": "getInlineFileControllerApiInlinefilecontroller",
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "OK",
            "headers": {
              "Content-Length": {
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "406": {
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

File attachments#

Set as_attachment=True when Django’s django.http.FileResponse is returned as an attachment. In this mode Content-Disposition is always set and usually contains the filename sent to the client:

Run result

$ curl http://127.0.0.1:8000/api/file/ -D - -X GET
HTTP/1.1 200 OK
date: Mon, 05 Oct 2026 11:18:51 GMT
server: uvicorn
Content-Type: text/plain
Content-Length: 16
Content-Disposition: attachment; filename="receipt.txt"
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

Example receipt

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"
      }
    }
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/filecontroller/": {
      "get": {
        "operationId": "getFileControllerApiFilecontroller",
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "OK",
            "headers": {
              "Content-Disposition": {
                "required": true,
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "406": {
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

The difference comes from the Content-Disposition HTTP header: it tells clients whether the response body is expected to be displayed inline or downloaded as an attachment.

API Reference#

class dmr.files.FileBodyLike[source]#

Interface that describes objects that can return media type schema.

Added in version 0.15.0.

abstractmethod classmethod get_schema(schema: Schema, context: OpenAPIContext) → Schema[source]#

Return the OpenAPI schema for this file body.

abstractmethod classmethod media_type(schema: Schema, model: TypeForm[Any], model_meta: tuple[Any, ...], metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], parser: Parser, context: OpenAPIContext) → MediaType[source]#

Provides file request schema for this parser.

Changed in version 0.16.0: Now accepts metadata and controller_cls parameters.

class dmr.files.FileBody[source]#

Bases: FileBodyLike

Special type that indicates that response returns a file body.

classmethod get_schema(schema: Schema, context: OpenAPIContext) → Schema[source]#

Returns the openapi schema that this object represents.

classmethod media_type(schema: Schema, model: TypeForm[Any], model_meta: tuple[Any, ...], metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], parser: Parser, context: OpenAPIContext) → MediaType[source]#

Returns the media type for the given file.

classmethod replace_schema(schema: Schema, context: OpenAPIContext) → Schema[source]#

Replaces existing generated schema with file-like schema.

Here the most tricky part happens. When we define FileMetadata[Info] as a model to parse, we don’t want to expose Info as a model actually, we want to show that this is a file in the OpenAPI schema. So, we place known models with the file specification here.

Unions like Model | None have their members replaced.

Added in version 0.15.0.

class dmr.files.FileResponseSpec(return_type: type[FileBodyLike] = <class 'dmr.files.FileBody'>, *, status_code: HTTPStatus = HTTPStatus.OK, headers: Mapping[str, ~dmr.headers.HeaderSpec] | None=None, cookies: Mapping[str, CookieSpec] | None=None, limit_to_content_types: Set[str] | None = None, streaming: bool = False, description: str | Promise | None = None, summary: str | Promise | None = None, links: Mapping[str, Link | Reference] | None=None, x_extensions: Mapping[str, ~typing.Any] | None=None, as_attachment: bool = False)[source]#

Bases: ResponseSpec

Special ResponseSpec subclass for files.

Unlike regular ResponspeSpec that will create a real schema for the return type, here we force to use dmr.files.FileBodyLike schema providers, that know how files will look like in the final schema.

as_attachment#

Marks responses with Content-Disposition header as required. Use together with FileResponse(as_attachment=True).

Type:

bool

Changed in version 0.10.0: Added as_attachment parameter that can mark files that should be sent via Content-Disposition header. Similar to Django’s as_attachment parameter in django.http.FileResponse.

Changed in version 0.15.0: Removed file_body attribute, now using return_type instead to generate the response schema.

__post_init__() → None[source]#

Set required headers depending on how files are returned.

get_schema(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], context: OpenAPIContext) → Response[source]#

Customize schema for the file response.

dmr.files.file_response_headers(headers: Mapping[str, HeaderSpec] | None, *, as_attachment: bool) → Mapping[str, HeaderSpec][source]#

Build headers expected from FileResponse.