Pydantic says that you can create custom classes by simply defining the __get_validators__ method. This is useful if you want to parse into a class with its own metaclass or for some other reason do not want to inherit from BaseModel.
However, this fails in strange places in FastAPI. For example, FastAPI does not detect such a class as a body parameter, but always thinks it is a query parameter.
from fastapi import FastAPI, Body
from fastapi.testclient import TestClient
app = FastAPI()
class NastyMetaClass(type):
pass
class Foo(metaclass=NastyMetaClass):
@classmethod
def __get_validators__(cls):
yield lambda value: True
@app.post("/implicit")
def foo(foo: Foo): # This is supposed to work, but does not
return "It worked"
@app.post("/explicit")
def foo_body(foo: Foo = Body(...)): # The `= Body(...)` fixes it
return "It worked"
client = TestClient(app)
response = client.post("/implicit", json={})
print(response.json())
# {'detail': [{'loc': ['query', 'foo'], 'msg': 'field required', 'type': 'value_error.missing'}]}
response = client.post("/explicit", json={})
print(response.json())
# It worked
How can I make FastAPI recognize custom Pydantic classes?
As per FastAPI documentation, when using Body(...) you instruct FastAPI to treat a parameter as a body key. Thus, using foo: Foo = Body(...) is one way to tell your endpoint to expect a JSON body with the attributes of a Foo.
Alternatively, you could delcare the Foo parameter using Dependencies, as shown below:
from fastapi import Depends
@app.post("/implicit")
def foo(foo: Foo = Depends(Foo)): # This should work
return "It worked"
You could even simply use Depends() (i.e., foo: Foo = Depends()) as a shortcut to avoid code repetition.