Fastapi: [BUG] Unable to have List of Pydantic Model as route 'responses' model

Created on 17 Oct 2019  路  9Comments  路  Source: tiangolo/fastapi

Describe the bug
When using responses={...: {"model": List[PydanticModel]}} in a route function there is an exception returned AssertionError: A response model must be a Pydantic model when attempting to run the application.

To Reproduce

from fastapi import FastAPI
from pydantic import BaseModel

class T(BaseModel):
    a: int


app = FastAPI()

@app.get("/", responses={200: {"model": List[T]}})
def index():
    ... # This is never entered due to error when adding the route

Expected behavior
This behaves the same as response_model= since it is allowed and works as intended there.

Environment:

  • OS: macOS
  • FastAPI Version [e.g. 0.3.0], get it with: fastapi==0.42.0
  • Python 3.7.0

Additional Context
I was going to return my response directly but would like the schema to be automatically done based on the pydantic model. I was following the steps in https://fastapi.tiangolo.com/tutorial/additional-responses/

bug confirmed good first issue

Most helpful comment

The "model" for the response should be the actual type returned with the response. response_model is basically a shorthand for what to use in the case where everything goes as planned, but, for example, if you wanted to return a list of errors in the case of a 400, you'd want to have responses={400: {"model": List[ErrorModel]}}.

It seems to me like you are concerned with the schema making sense as a human, but the point is for it to be correct if interpreted by a machine (e.g., for client generation).

I don't see any reason why this version:

@app.get("/", responses={200: {"model": List[T]}})
def index():
    ... # This is never entered due to error when adding the route

shouldn't result in the same openapi schema as this version:

@app.get("/", response_model=List[T])
def index():
    ... # This is never entered due to error when adding the route

My suspicion is that this is only broken right now because most people don't frequently use this capability, and so there hasn't been a push to fix it. I don't see any reason an arbitrary pydantic field couldn't be supported here.

Admittedly, I'm not 100% confident in my understanding of the responses argument, but looking through the code it looks like it is intended to be treated the same as the response_model (with response_model just getting automatically injected into the responses with status code 200).

All 9 comments

Hi @nwalsh1995
On your code:

  1. you should include response_model=T
  2. you should be using List in the response_model parameter

Try it out:

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

class T(BaseModel):
    a: int


app = FastAPI()
@app.get("/", response_model=List[T], responses={200: {"model": T}})
def index():
    ... # This is never entered due to error when adding the route

@nwalsh1995 I've updated my suggestion, please take another look if you've already read it. Thanks

@iamaldi But isn't that the wrong response? Why would you want to document it as returning a T instead of a List[T]?

If I understand correctly, this seems like a bug, but I think it shouldn't be too hard to fix.

@dmontagu Well, by writing response_model=List[T] you're basically saying that the responses should be a list of T then inside the responses, each model is going to be of model T.

Could you ellaborate a bit more on why this seems like a bug? I read all the available documentation and saw response_model=List[T] as the only available option to do this.

Thanks

The "model" for the response should be the actual type returned with the response. response_model is basically a shorthand for what to use in the case where everything goes as planned, but, for example, if you wanted to return a list of errors in the case of a 400, you'd want to have responses={400: {"model": List[ErrorModel]}}.

It seems to me like you are concerned with the schema making sense as a human, but the point is for it to be correct if interpreted by a machine (e.g., for client generation).

I don't see any reason why this version:

@app.get("/", responses={200: {"model": List[T]}})
def index():
    ... # This is never entered due to error when adding the route

shouldn't result in the same openapi schema as this version:

@app.get("/", response_model=List[T])
def index():
    ... # This is never entered due to error when adding the route

My suspicion is that this is only broken right now because most people don't frequently use this capability, and so there hasn't been a push to fix it. I don't see any reason an arbitrary pydantic field couldn't be supported here.

Admittedly, I'm not 100% confident in my understanding of the responses argument, but looking through the code it looks like it is intended to be treated the same as the response_model (with response_model just getting automatically injected into the responses with status code 200).

@dmontagu Now I can see what the issue was in the first place. I was initially confused and thought this was a implementation-specific issue @nwalsh1995 had.

Thanks for the clarification.

Yep. Response models in the extra responses should receive any type valid for a Pydantic field, not only Pydantic models. That's a valid bug/feature request.

This was fixed by @patrickmckenna in https://github.com/tiangolo/fastapi/pull/1017.

It will be available in the next release. :rocket: :tada:

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

Was this page helpful?
0 / 5 - 0 ratings