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.

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: Tue, 26 May 2026 19:09:13 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"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/inlinefilecontroller/": {
      "get": {
        "deprecated": false,
        "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"
          }
        }
      }
    }
  }
}

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: Tue, 26 May 2026 19:09:13 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"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/filecontroller/": {
      "get": {
        "deprecated": false,
        "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.FileBody[source]

Special type that indicates that response returns a file body.

classmethod encoding(model: Any, schema: Schema) dict[str, Encoding] | None[source]

Returns the openapi encoding for the defined media type.

classmethod get_schema(schema: Schema | Reference, context: OpenAPIContext) Schema[source]

Returns the openapi schema that this object represents.

classmethod media_type(schema: Schema | Reference, model: Any, model_meta: tuple[Any, ...], parser: Parser, context: OpenAPIContext) MediaType[source]

Returns the media type for the given file.

class dmr.files.FileResponseSpec(return_type: type[FileBody] = <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 | None = None, links: dict[str, Link | Reference] | None=None, as_attachment: bool = False, file_body: type[FileBody] = <class 'dmr.files.FileBody'>)[source]

Special ResponseSpec subclass for files.

as_attachment

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

Type:

bool

file_body

Model to be used for file body schema generation.

Type:

type[dmr.files.FileBody]

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.

__post_init__() None[source]

Set required headers depending on how files are returned.

get_schema(metadata: EndpointMetadata, serializer: type[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.