Fastapi: [BUG] Union doesn't document properly on swagger

Created on 5 Mar 2020  ยท  12Comments  ยท  Source: tiangolo/fastapi

Describe the bug

When using Union on response_model, it doesn't doucment properly on swagger.

To Reproduce

  1. Create two models
from pydantic import BaseModel

class VerifyTokenResponseSchema(BaseModel):
    status: bool

class ExceptionResponseSchema(BaseModel):
    error: str
  1. Add it on response_model
from typing import Union
from fastapi import APIRouter

oauth_router = APIRouter()

@oauth_router.post('/verify', response_model=Union[VerifyTokenResponseSchema, ExceptionResponseSchema])
async def verify_token(token: str):
    status = await VerifyTokenUsecase().execute(token=token)
    return status.dict()
  1. Check it on /docs

Expected behavior

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2020-03-05 แ„‹แ…ฉแ„’แ…ฎ 7 59 47

Example value tab throw no example available.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2020-03-05 แ„‹แ…ฉแ„’แ…ฎ 7 59 57

But it can show on Schema tab

Additional context

I want to check both response schema on Example value tab.

Is there anything wrong with my code?

(I know that I can use responses but using Union is described on document(https://fastapi.tiangolo.com/tutorial/extra-models/#union-or-anyof)

bug

All 12 comments

What do you want to display in example value like Union type

FastAPI does not generate body examples, that's just something SwaggerUI does, and Swagger apparently doesn't create an example value for type unions (probably because there's no single valid schema anymore). The union is technically properly documented (the schema tab is still correct, and that's the one that actually matters).

I don't know if it's currently possible to specify a custom example structure using FastAPI, though.

I'm not familiar with swagger, but I think it's necessary to obey the swagger rules by default.
Sure, you can define a custom structure, but how to display it that's not easy, two and more example value?

Swagger is just the documentation renderer FastAPI exposes at localhost:8000/docs. FastAPI also embeds another documentation renderer, ReDoc, which is accessible at localhost:8000/redoc and might support type unions better than Swagger, but I'm not entirely sure.

Normally, if Swagger can't figure out an example for your schema, your OpenAPI schema can specify one, and Pydantic supports it for individual BaseModel classes, but unions are trickier because they're not actually classes...

Thanks for the help here @Dustyposa and @sm-Fifteen ! :clap: :cake:

Yep, indeed that's not a bug in FastAPI, but a feature request for Swagger UI.

Nevertheless, you can add a custom example that is shown in Swagger UI, check the docs: https://fastapi.tiangolo.com/tutorial/schema-extra-example/

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

@Dustyposa ์™€ @ sm-Fifteen ์˜ ๋„์›€์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค !๐Ÿ‘ ๐Ÿฐ

์‹ค์ œ๋กœ FastAPI์˜ ๋ฒ„๊ทธ๋Š” ์•„๋‹ˆ์ง€๋งŒ Swagger UI์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  Swagger UI์— ํ‘œ์‹œ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์ œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. https://fastapi.tiangolo.com/tutorial/schema-extra-example/

class Human(BaseModel):
    age: int
    name: str


class Man(Human):


class Woman(Human):


HumanCreateRequest = Union[Man, Woman]

# POST /human

I want to set default schema Woman class in swagger.

but swagger example is empty.

@mcauto Please see comments above. It's not supported by Swagger UI so you have to add a custom example. Also -

What do you want to display in example value like Union type?

from fastapi import FastAPI, Body
from typing import Union
from pydantic import BaseModel


class User(BaseModel):
    name: str


class Item(BaseModel):
    size: int
    price: float


app = FastAPI()

process_things_body_example = {"name": "1", "size": 2, "price": 3} # whatever makes sense to you

@app.post("/multi/")
def process_things(body: Union[User, Item] = Body(..., example=process_things_body_example)):
    return body

Please create an issue at https://github.com/swagger-api/swagger-ui if you want this behavior to be improved.

@mcauto Please see comments above. It's not supported by Swagger UI so you have to add a custom example. Also -

What do you want to display in example value like Union type?

from fastapi import FastAPI, Body
from typing import Union
from pydantic import BaseModel


class User(BaseModel):
    name: str


class Item(BaseModel):
    size: int
    price: float


app = FastAPI()

process_things_body_example = {"name": "1", "size": 2, "price": 3} # whatever makes sense to you

@app.post("/multi/")
def process_things(body: Union[User, Item] = Body(..., example=process_things_body_example)):
    return body

Please create an issue at https://github.com/swagger-api/swagger-ui if you want this behavior to be improved.

Thank you for your attension. ๐Ÿ‘๐Ÿ‘

I solved it!

class Human(BaseModel):
    age: int
    name: str


class Man(Human):

class Woman(Human):

class HumanCreateRequest(BaseModel):
    target: Union[Woman, Man]
    class Config:
        schema_extra = {
            "example": {
                "target": Man(age=29, name="deo").dict()
            }
        }

app = FastAPI()

@app.post("/human")
def add_human(create_request: HumanCreateRequest):
    pass
# POST /human

Hi! @phy25

Have you ever been like this?

from typing import Union

from fastapi.applications import FastAPI
from pydantic import BaseModel


class Human(BaseModel):
    age: int
    name: str


class Man(Human):
    """ man """

    something: str


class Woman(Human):
    """ woman """


class ManCreateRequest(Man):
    class Config:
        schema_extra = {
            "example": {
                "target": Man(age=29, name="deo", something="something").dict()
            }
        }


class HumanCreateRequest(BaseModel):
    target: Union[Woman, Man]

    class Config:
        schema_extra = Man.Config.schema_extra # default Schema Man


app = FastAPI()


@app.post("/human")
def add_human(create_request: HumanCreateRequest) -> None:
    instance_type = create_request.target # always Woman
    return

Pydantic will always return the first one...

image

I want to get real type of HumanCreateRequest.

Was this page helpful?
0 / 5 - 0 ratings