Fastapi: [BUG] KeyError using Pydantic dataclass as request body parameter

Created on 14 Oct 2019  路  7Comments  路  Source: tiangolo/fastapi

Describe the bug
If a user attempts to use the dataclass from Pydantic to decorate the class used in a request body, a KeyError is thrown when loading the OpenAPI docs.

To Reproduce
Steps to reproduce the behavior:

  1. Create a file with this example:
from fastapi import FastAPI
from pydantic.dataclasses import dataclass


@dataclass()
class Item:
    name: str
    price: float
    description: str = None
    tax: float = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
  1. Start the app server
  2. Open the OpenAPI docs in a browser (e.g. http://127.0.0.1:8000/docs)
  3. See error in terminal KeyError: <class 'src.main.Item'>

Expected behavior
I expect this to work the same as if I had declared my Item class with Pydantic's BaseModel.
Here's an example that works as expected:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: float
    description: str = None
    tax: float = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

Environment:

  • OS: macOS
  • FastAPI Version: 0.42.0

    • Python version, get it with: 3.7.3

Additional context
I stumbled across this while trying to adapt this example from the docs to use Pydantic.dataclasses instead of Pydantic.BaseModel.

bug

Most helpful comment

+1 for supporting pydantic dataclasses as request bodies!

All 7 comments

It looks like the pydantic dataclasses are not currently supported; it looks like there are a lot of places where changes would need to be added in the fastapi code base to make them work.

In theory it shouldn't be too complicated to add support everywhere necessary (though it might be a lot of effort, especially to write the appropriate tests). But I'm not sure if there would be performance implications to all the additional checks that would be necessary, which would be unfortunate.

Probably best to get @tiangolo's opinion about this before anyone starts work integrating pydantic dataclass support; I'm not sure whether the increase in maintenance burden (which would definitely be non-trivial) would be worth it.

Either way, it should probably wait for v1.0 of pydantic.

Ouch, what a bummer! After I saw your advice to use the ORM mode in https://github.com/tiangolo/fastapi/issues/265#issuecomment-557849193, I had pretty much everything working with dataclasses. This issue seems to be the last thing that prevents to use dataclasses with FastAPI.

@ramnes The above comment is now out of date (for starters, pydantic v1 is now released!).

The example seems to work for me now, with the caveat that you have to manually specify that a pydantic dataclass should be read from the body:

from pydantic.dataclasses import dataclass
from starlette.testclient import TestClient

from fastapi import Body, FastAPI


class Config:
    orm_mode = True


@dataclass(config=Config)
class Item:
    name: str
    price: float
    description: str = None
    tax: float = None


app = FastAPI()


@app.post("/items/", response_model=Item)
async def create_item(item: Item = Body(...)) -> Item:  # <----- have to add `= Body(...)` here
    return item


print(TestClient(app).post("/items/", json={'name': 'name', 'price': 0.01}).json())
# {'name': 'name', 'price': 0.01, 'description': None, 'tax': None}

I think it could make sense to automatically infer that pydantic dataclasses should be read from the body, rather than query parameters (which is what happens currently). If that's of interest to you, please create a feature request.

+1 for supporting pydantic dataclasses as request bodies!

+1 for native support.
I also noticed a slightly strange behaviour on GET example request. Here, fields are already destructed. Yet, the request fails with missing the parameter name. Is this intended?

from fastapi import FastAPI, Body
from pydantic.dataclasses import dataclass
from pydantic import HttpUrl, conint

class Config:
    orm_mode = True

@dataclass(config=Config)
class ImageRef:
    url: HttpUrl
    width: conint(gt=0)
    height: conint(gt=0)

app = FastAPI()

@app.post("/items/working/", response_model=ImageRef)
def hello_img1(img: ImageRef=Body(...)):
    return img

@app.get("/items/confused_docs/")
def hello_img2(img: ImageRef):
    return img

I am using dataclasses in python 3.8 and the same problem.

same on python 3.9

Was this page helpful?
0 / 5 - 0 ratings