-
First Check
Commit to Help
Example Codefrom fastapi import FastAPI
from pydantic import BaseModel,Field
import typing
app = FastAPI()
class JustLovelyName(BaseModel):
name: typing.Union[str,None] = None
@app.get("/output")
def output() -> JustLovelyName:
return JustLovelyName(name="Hello World")
@app.post("/input")
def two(name:JustLovelyName) -> JustLovelyName:
return name
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8002) DescriptionOpenAPI for This happens when a Model has a default value (apparently In version removing the usage as input or removing the default: Operating SystemLinux Operating System DetailsLXC container FastAPI Version0.101.0 Pydantic Version2.1.1 Python Version3.11.2 Additional Context |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments 12 replies
-
related #10039 #10041 |
Beta Was this translation helpful? Give feedback.
-
I added the above code as a test from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI()
class JustLovelyName(BaseModel):
name: Union[str, None] = None
@app.get("/output")
def output() -> JustLovelyName:
return JustLovelyName(name="Hello World")
@app.post("/input")
def two(name: JustLovelyName) -> JustLovelyName:
return name
client = TestClient(app)
def test_get_output():
response = client.get("/output")
assert response.status_code == 200, response.text
def test_post_input():
response = client.post("/input", json={"name": "Hello World"})
assert response.status_code == 200, response.text
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {...} to run |
Beta Was this translation helpful? Give feedback.
-
@tiangolo Is there any chance we could be allowed to disable this new functionality? This change made our api generator (which is the one recommended by FastAPI docs) suddenly delete all our generated model files, and replace each one with |
Beta Was this translation helpful? Give feedback.
-
Can be reproduced with pydantic only. @Kludex any ideas how to work this? import io
import yaml
from pydantic import BaseModel
from pydantic.json_schema import models_json_schema
class Pet(BaseModel):
name: str
weight: int | None
color: str | None = None
data = models_json_schema([(Pet,"serialization"),(Pet,"validation")])
f = io.StringIO()
for d in data:
for k,v in d.items():
print(yaml.dump(v, f, indent=4))
f.seek(0)
print(f.read()) $ref: '#/$defs/PetOutput'
$ref: '#/$defs/PetInput'
PetInput:
properties:
color:
anyOf:
- type: string
- type: 'null'
default: null
title: Color
name:
title: Name
type: string
weight:
anyOf:
- type: integer
- type: 'null'
title: Weight
required:
- name
- weight
title: Pet
type: object
PetOutput:
properties:
color:
anyOf:
- type: string
- type: 'null'
default: null
title: Color
name:
title: Name
type: string
weight:
anyOf:
- type: integer
- type: 'null'
title: Weight
required:
- name
- weight
- color
title: Pet
type: object |
Beta Was this translation helpful? Give feedback.
-
Yep, so this is actually intentional. 😅 There was a lot of work from the Pydantic side and here to make it work this way... let me explain. The idea is that an input model can have fields that are not required if they have default values. But if you return that same model, because it will always have a value (if you don't provide one it will have the default value), then the clients could expect that it will always have a value. Nevertheless, this was not reflected in OpenAPI up to now. The way to do that is by saying that the response model with a field that has a default value is required. This is in particular useful if you are autogenerating client code/SDKs, because now that code will be more correct. And the developers using that client code/SDKs won't have to add a bunch of checks to see if the value is provided or not, they will be able to simplify their code and know that there will always be a value, because the type information will include that. Now, given that several would prefer to keep the old behavior, probably/hopefully just for a bit before upgrading the client code, I added a feature to allow telling FastAPI to not separate models with It's available in FastAPI 0.102.0, just released, and there are new docs for that here: https://fastapi.tiangolo.com/how-to/separate-openapi-schemas/ 🎉 |
Beta Was this translation helpful? Give feedback.
-
This entire change that got introduced in FastAPI 0.101.0 seems extremely fragile to me. You may refactor your code in your FastAPI service and not even realize that one schema name might be removed and two new ones will be created in its place. This is very unexpected and fragile behavior. If the above is true, there will be a high probability something breaks for e.g. clients generated from the OpenAPI schema. |
Beta Was this translation helpful? Give feedback.
-
@tiangolo I am using Because of this, I had to downgrade to FastAPI 0.100.1 here. Also, see my comment here on that this OpenAPI codegen change seems extremely fragile to me. |
Beta Was this translation helpful? Give feedback.
Yep, so this is actually intentional. 😅
There was a lot of work from the Pydantic side and here to make it work this way... let me explain.
The idea is that an input model can have fields that are not required if they have default values.
But if you return that same model, because it will always have a value (if you don't provide one it will have the default value), then the clients could expect that it will always have a value. Nevertheless, this was not reflected in OpenAPI up to now. The way to do that is by saying that the response model with a field that has a default value is required.
This is in particular useful if you are autogenerating client code/SDKs, because now that code wil…