pythonswaggerfastapiopenapiswagger-ui

How to display both application/json and application/octet-stream content types in Swagger UI autodocs in FastAPI?


I have an endpoint that can return JSON or a file (xlsx):

class ReportItemSerializer(BaseModel):
    id: int
    werks: Annotated[WerkSerializerWithNetwork, Field(validation_alias="werk")]
    plu: Annotated[str, Field(description="Название PLU")]
    start_date: Annotated[date, None, Field(description="Дата начала периода в формате")]
    end_date: Annotated[date | None, Field(description="Дата окончания периода")]
    anfmenge: Annotated[int | None, Field(des cription="Запас на начало периода")]
    endmenge: Annotated[int | None, Field(description="Остаток по PLU в разрезе поставщика на конец периода")]
    soll: Annotated[int, None, Field(description="Поступило за период")]
    haben: Annotated[
        int | None, Field(description="Количество по PLU, которое было возвращено поставщику за указанный период")]
    model_config = ConfigDict(from_attributes=True)


class PaginatedReportItemsSerializer(BaseModel):
    count: int
    results: list[ReportItemSerializer]


@router.get(
    "/orders/report",
    responses={
        200: {
            "description": "Return report",
            "content": {
                "application/json": {
                    "schema": PaginatedReportItemsSerializer.schema(ref_template="#/components/schemas/{model}")
                },
                "application/octet-stream": {}
            },
        },
    }
)
async def get_report():
    pass

With this I have a problem with swagger. With configuration above there is problem:

enter image description here

Nested ReportItemSerializer is not being displayed (string is instead displayed).

How can I fix it?


Solution

  • Here is a working example, heavily based on this answer—hence, please have a look at that answer for more details.

    The example below will correctly display the example value/schema in Swagger UI autodocs, under the 200 response's description. Using the drop down menu, one could switch between the various media types; in this case, that is application/json and application/octet-stream.

    Working Example

    from fastapi import FastAPI
    from fastapi.openapi.constants import REF_PREFIX
    from pydantic import BaseModel
    from datetime import datetime
    
    
    class SubMessage(BaseModel):
        msg: str
        dt: datetime = None
    
    
    class Message(BaseModel):
        msg: str
        sub: SubMessage
    
    
    def get_200_schema():
        return {
            'model': Message,
            'content': {
                'application/json': {
                    'schema': {'$ref': REF_PREFIX + Message.__name__}
                },
                'application/octet-stream': {
                    'schema': {} # whatever
                }
            },
        }
    
    
    app = FastAPI()
    
    
    @app.get('/', responses={200: get_200_schema()})
    async def get_msg():
        return Message(msg='main msg', sub=SubMessage(msg='sub msg', dt=datetime.now()))