-
Is FastAPI
Important: this is only for code that was working before and then after upgrading FastAPI, not for something that is not working for you since previous versions of FastAPI. As FastAPI now supports Pydantic v2, any problems are not about the beta (as it's already released 😅), so let's keep the normal workflow. If you are having problems, please create a new Discussion question following the template! 🙏 https://github.com/tiangolo/fastapi/discussions/new?category=questions |
Beta Was this translation helpful? Give feedback.
Replies: 66 comments 257 replies
-
Everything is working fine! 🎉 Add an upvote and thumbs up here if everything is working fine for you. Optionally, add a comment. 😄 |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
SQLModel test suite fails with this kind of differences in the openapi schema, using Pydantic v1. 11 fails under
fastapi 0.100.0b1 Otherwise unmodified SQLModel main (same as 0.0.8 release), only changed the fastapi dep version.
|
Beta Was this translation helpful? Give feedback.
-
Parsing of Literal types seems to be broken with Pydantic v2. Take this example: from typing import Literal
from pydantic import BaseModel
class Health(BaseModel):
status: Literal["ok", "error"] Doing However, if I use it as the return model for a path operation, the schema is broken in the swagger docs. Weirdly the examples do use one of the values. Here is a pastebin with the generated openapi.json: https://pastebin.com/a95Tujah |
Beta Was this translation helpful? Give feedback.
-
I use pydantic HEAD PydanticUndefinedwas changed in pydantic/pydantic@f742ca4 invalid OpenAPI description document when using nested discriminated unionsthe OpenAPI document created fails internal validation
it's the result of nested discriminated unions, can be reproduced by using Pet in a fastapi service as body argument: from datetime import timedelta
import uuid
import sys
if sys.version_info >= (3, 9):
from typing import List, Optional, Literal, Union, Annotated
else:
from typing import List, Optional, Union
from typing_extensions import Annotated, Literal
import pydantic
from pydantic import BaseModel, RootModel, Field
from pydantic_core import PydanticUndefined as Undefined
class PetBase(BaseModel):
identifier: Optional[str] = Field(default_factory=lambda: str(uuid.uuid4()))
name: str
tags: Optional[List[str]] # = Field(default_factory=list)
class BlackCat(PetBase):
pet_type: Literal["cat"] = "cat"
color: Literal["black"] = "black"
black_name: str
class WhiteCat(PetBase):
pet_type: Literal["cat"] = "cat"
color: Literal["white"] = "white"
white_name: str
Cat = Annotated[Union[BlackCat, WhiteCat], Field(default=Undefined, discriminator='color')]
class Dog(PetBase):
pet_type: Literal["dog"] = "dog"
name: str
age: timedelta
class Pet(RootModel):
root: Annotated[Union[Cat, Dog], Field(discriminator="pet_type")]
class Pets(RootModel):
root: List[Pet] = Field(..., description="list of pet") fails at Pet:
discriminator:
mapping:
cat:
discriminator:
mapping:
black: '#/components/schemas/BlackCat'
white: '#/components/schemas/WhiteCat'
propertyName: color
oneOf:
- $ref: '#/components/schemas/BlackCat'
- $ref: '#/components/schemas/WhiteCat'
dog: '#/components/schemas/Dog'
propertyName: pet_type
oneOf:
- discriminator:
mapping:
black: '#/components/schemas/BlackCat'
white: '#/components/schemas/WhiteCat'
propertyName: color
oneOf:
- $ref: '#/components/schemas/BlackCat'
- $ref: '#/components/schemas/WhiteCat'
- $ref: '#/components/schemas/Dog'
title: Pet
|
Beta Was this translation helpful? Give feedback.
-
I used rootmodels as I was unaware of the limitations.
from typing import Literal, Union
from typing_extensions import Annotated
from pydantic import BaseModel, Field, ValidationError
class BlackCat(BaseModel):
pet_type: Literal['cat']
color: Literal['black']
black_name: str
class WhiteCat(BaseModel):
pet_type: Literal['cat']
color: Literal['white']
white_name: str
Cat = Annotated[Union[BlackCat, WhiteCat], Field(discriminator='color')]
try:
from pydantic._internal._fields import Undefined as Undefined
except:
from pydantic_core import PydanticUndefined
import pydantic._internal._fields
pydantic._internal._fields.Undefined = PydanticUndefined
pydantic._internal._fields._UndefinedType = type(PydanticUndefined)
from fastapi import FastAPI, Response, Body
app = FastAPI()
@app.post(
"/pet",
operation_id="createPet",
response_model=Cat,
responses={201: {"model": Cat}},
)
def createPet(
response: Response,
pet: Cat = Body(..., embed=True),
) -> None:
return pet |
Beta Was this translation helpful? Give feedback.
-
Will this be a minor release or a major release? I note that this version is 0.100.0 instead of 1.0.0. I'm wondering if I need to disable automatic version updates for FastAPI using Renovate. |
Beta Was this translation helpful? Give feedback.
-
Hey @tiangolo What's the goal down the road? To drop Pydantic v1? I'm sorry I couldn't find anything explaining the direction long term. Thanks, |
Beta Was this translation helpful? Give feedback.
-
The use of "null" as type requires "3.1.0" as OpenAPI version in the description document.
from pydantic import BaseModel
class A(BaseModel):
data: str | None
schema = A.model_json_schema()
assert any(map(lambda x: x["type"] == "null", schema["properties"]["data"]["anyOf"])) |
Beta Was this translation helpful? Give feedback.
-
Using pydantic/pydantic@70a43f7 Can not use discriminated unions as body arguments
from typing import Literal, Union, List
from typing_extensions import Annotated
from pydantic import BaseModel, Field
class BlackCat(BaseModel):
pet_type: Literal['cat']
color: Literal['black']
black_name: str
class WhiteCat(BaseModel):
pet_type: Literal['cat']
color: Literal['white']
white_name: str
Cat = Annotated[Union[BlackCat, WhiteCat], Field(discriminator='color')]
class Dog(BaseModel):
pet_type: Literal['dog']
name: str
Pet = Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]
Pets = List[Pet]
# patching for fastapi
from pydantic_core import PydanticUndefined
import pydantic._internal._fields
pydantic._internal._fields.Undefined = PydanticUndefined
pydantic._internal._fields._UndefinedType = type(PydanticUndefined)
from fastapi import FastAPI, Response, Body, Path
app = FastAPI()
@app.post(
"/pet",
operation_id="createPet",
)
def createPet(
response: Response,
pet: Pet = Body(..., embed=True),
# pet: Annotated[Pet, Body(embed=True)]
) -> Pet:
return pet Invalid description document to do discrimination target resolving instead of referencingUsing Dog for the Body in the example- "it works" but the description document is invalid due to the same problem as mentioned before - responses:
'200':
content:
application/json:
schema:
discriminator:
mapping:
cat:
discriminator:
mapping:
black: '#/components/schemas/BlackCat'
white: '#/components/schemas/WhiteCat'
propertyName: color
oneOf:
- $ref: '#/components/schemas/BlackCat'
- $ref: '#/components/schemas/WhiteCat'
dog: '#/components/schemas/Dog'
propertyName: pet_type
oneOf:
- discriminator:
mapping:
black: '#/components/schemas/BlackCat'
white: '#/components/schemas/WhiteCat'
propertyName: color
oneOf:
- $ref: '#/components/schemas/BlackCat'
- $ref: '#/components/schemas/WhiteCat'
- $ref: '#/components/schemas/Dog'
title: Response Createpet
description: Successful Response I'd expect it to be responses:
'200':
content:
application/json:
schema:
discriminator:
mapping:
cat: '#/components/schemas/Cat'
dog: '#/components/schemas/Dog'
propertyName: pet_type
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
title: Response Createpet
description: Successful Response |
Beta Was this translation helpful? Give feedback.
-
@tiangolo , can you add new |
Beta Was this translation helpful? Give feedback.
-
I detected when pydantic v2 class has a override model_validate (ex from_orm) the endpoints crash with a ResponseValidationError Full example: requirements: requirements: Run: # main.py
from typing import Any
from sqlalchemy import ForeignKey, Integer, String, create_engine
from sqlalchemy.orm import (
Mapped,
declarative_base,
mapped_column,
relationship,
sessionmaker,
)
# SQLAlchemy
engine = create_engine("sqlite:///test.db")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True, nullable=False)
email: Mapped[str] = mapped_column(String, nullable=False, unique=True)
posts: Mapped[list["Post"]] = relationship(back_populates="owner")
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(primary_key=True, nullable=False)
title: Mapped[str] = mapped_column(String(255), nullable=False)
owner_id: Mapped[int] = mapped_column(
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False
)
owner: Mapped["User"] = relationship(back_populates="posts")
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
# Init db
db = next(get_db())
user1 = User(email="[email protected]")
user2 = User(email="[email protected]")
post1_user1 = Post(title="Post 1 by User 1", owner=user1)
post2_user1 = Post(title="Post 2 by User 1", owner=user1)
post1_user2 = Post(title="Post 1 by User 2", owner=user2)
post2_user2 = Post(title="Post 2 by User 2", owner=user2)
user1.posts.extend([post1_user1, post2_user1])
user2.posts.extend([post1_user2, post2_user2])
db.add(user1)
db.add(user2)
db.commit()
# Schema pydantic
from pydantic import BaseModel, Field
class UserOut(BaseModel):
id: int = Field(description="ID")
email: str = Field(description="Email")
first_post_title: str = Field(description="First post title")
class Config:
orm_mode = True
## Pydantic v1 *********************
## With fastapi 0.99.1 and pydantic v1 = OK
@classmethod
def from_orm(cls, obj: Any) -> "UserOut":
if hasattr(obj, "posts"):
obj.first_post_title = obj.posts[0].title
return super().from_orm(obj)
## Pydantic v2 *********************
## With fastapi 0.100.0b2 and pydantic v2 = ResponseValidationError
@classmethod
def model_validate(cls, obj: Any) -> "UserOut":
if hasattr(obj, "posts"):
obj.first_post_title = obj.posts[0].title
return super().model_validate(obj)
# Fastapi
from fastapi import Depends, FastAPI
from sqlalchemy import select
from sqlalchemy.orm import Session
app = FastAPI()
@app.get("/")
async def root(
db: Session = Depends(get_db),
) -> UserOut:
user = db.execute(select(User)).scalars().first()
return user |
Beta Was this translation helpful? Give feedback.
-
Not entirely sure if this is already addressed, but after the release of This code produces a valid schema with from typing import Literal
from devtools import debug
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Request(BaseModel):
at: Literal["today", "tomorrow", "yesterday"]
@app.get("/")
def root(data: Request):
return data
debug(app.openapi()) The diff is --- out_2.0.txto2023-07-05 12:27:09
+++ out_2.0.1.txt 2023-07-05 12:29:05
@@ -14,7 +14,7 @@
'content': {
'application/json': {
'schema': {
- '$ref': '#/components/schemas/Request',
+ '$ref': '#/components/schemas/__main____RequestInput__1',
},
},
}, … and of course |
Beta Was this translation helpful? Give feedback.
-
Going through pydantic docs to try to discover the root cause of an issue I am seeing. Previously you could do
According to pydantic v2 docs dict -> model_dump and it still supports exclude_none etc. But in fastapi responses I am now seeing the keys with None values appearing, and I can't track down where the disconnect between myself, fastapi and pydantic v2 lies. I've tried overloading every method I can detect that is called during fastapi serialization to see if any of them are called, but it seems to not be hitting any of the pydantic methods. Despite the following code, the endpoint returns as if nothing has gone wrong. Any assistance would be great, looking forward to upgrading our systems to fastapi with pydantic v2, but can't seem to get past this one on my own. Minimum example
|
Beta Was this translation helpful? Give feedback.
-
Hello ! After migrating to the latest version of FastAPI, a feature related to Multipart Form has stopped working. Previously, when using FastAPI version 0.99.1 and Pydantic 1.10.11, the code below functioned correctly:
I was able to send POST requests with Multipart Form data like this, with a string and a list of tags: However, after migrating to the latest versions of FastAPI and Pydantic (0.100.0 and 2.0.3 respectively), the same request now returns validation errors for both fields: I have gone through the migration guide, but I was unable to find any relevant information. Could this be an issue with FastAPI or Pydantic? Is there a way to modify the code to make it work? Thank you for your help! |
Beta Was this translation helpful? Give feedback.
-
We are having an error with fastapi 0.100, pydantic 2.0.3. We have a regex with a lookaround, which can't be used anymore in V2. Pydantic have a workaround, but it doesn't seem to work with fastapi. import re
from dataclasses import dataclass
from typing import Any, Annotated
import uvicorn
from fastapi import FastAPI
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import CoreSchema, PydanticCustomError, core_schema
PERIOD_REGEX = (
# Regex to validate that a string is an ISO-8601 format duration. See
# https://en.wikipedia.org/wiki/ISO_8601#Time_intervals for more information
# on duration only ISO-8601 time intervals.
r"^P(?!$)(\d+(?:\.\d+)?Y)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?W)?(\d+(?:\.\d+)?D)?"
r"(T(?=\d)(\d+(?:\.\d+)?H)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?S)?)?$"
)
# Copied straight from pydantic docs
@dataclass
class Regex:
pattern: str
def __get_pydantic_core_schema__(
self, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
regex = re.compile(self.pattern)
def match(v: str) -> str:
if not regex.match(v):
msg = "string_pattern_mismatch"
raise PydanticCustomError(
msg,
"String should match pattern '{pattern}'",
{"pattern": self.pattern},
)
return v
return core_schema.no_info_after_validator_function(
match,
handler(source_type),
)
def __get_pydantic_json_schema__(
self, core_schema: CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
json_schema = handler(core_schema)
json_schema["pattern"] = self.pattern
return json_schema
# Our custom type
PERIOD_RESOLUTION_TYPE = Annotated[str, Regex(pattern=PERIOD_REGEX)]
app = FastAPI()
@app.get("/test")
def endpoint(period_resolution: PERIOD_RESOLUTION_TYPE):
"""Evaluate string JSON query param."""
return period_resolution
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000) The endpoint then takes any string. If someone can advise that would be great. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Hi, There is a bug in Pydantic v2.1.1 that impacts FastAPI. Response output is not validated correctly. See the following example where the second test fails. Issue reported in Pydantic here: pydantic/pydantic#6901 Pull Request to FastAPI adding the two tests that are copied below: #9954
|
Beta Was this translation helpful? Give feedback.
-
I have some issues with Decimals in new version. Now all Decimals are casted to strings instead of floats, and my external infrastructure is not yet ready for that: Why it works like that now: |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Upgrading to fastapi 0.100 I am seeing what looks like function call references in the View code: @app.get("/query-param-validation")
def get_query_param_validation(object_id: UUID):
return "OK" response with non-UUID object_id: before with fastapi 0.99.1 and pydantic 1.10.10:
after with 0.100.0, pydantic 2.0.3:
Expected behavior:
|
Beta Was this translation helpful? Give feedback.
-
Hi, Upgrading to fastapi ==0.100.1, Using Pipenv:
AWS Lambda Python 3.11 x86_64. Error Messsage: Reverting back to 0.99.1 resolves the issues. |
Beta Was this translation helpful? Give feedback.
-
Hello! Not sure if this was covered (I read through quite a few issues): I have an issue with OpenAPI and non required fields. Any idea? The following: #!/usr/bin/env python3
from fastapi import FastAPI, Depends, Query
from pydantic import BaseModel, Field
from typing import Annotated
import uvicorn
import os
app = FastAPI()
class MyInput(BaseModel):
foo: int|None = Field(title="hello world")
foodefault: int|None = Field(default=None,title="hello world")
foorequire: int = Field(title="hello required")
foodefaultnum: int = Field(default=0,title="hello world")
@app.get("/", response_model=MyInput)
async def root(ipt: MyInput = Depends()):
return ipt
if __name__ == "__main__":
uvicorn.run(app, port=int(os.environ.get("PORT", "8000"))) Requirements:
And
|
Beta Was this translation helpful? Give feedback.
-
Looks like classes as dependencies doesn't work anymore when the class is a pydantic model. It used to work before, see this StackOverflow answer.
@router.get(path='/api/endpoints')
async def endpoint(
schema: Annotated[SomeSchema, Depends()],
):
... This gives the following exception:
A workaround I found is to explicitly catch the pydantic error and convert it to fastapi's def validate_schema(request: Request) -> SomeSchema:
try:
return SomeSchema.model_validate(request.query_params)
except pydantic.ValidationError as exc:
raise RequestValidationError(exc.errors()) from None
@router.get(path='/api/endpoints')
async def endpoint(
schema: Annotated[SomeSchema, Depends(validate_schema)],
):
... |
Beta Was this translation helpful? Give feedback.
-
I have a problem using Annotated directly as a request param when using MongoDB _id. I updated my ObjectIdType to new pydantic as following:
Then I have the route:
That gives me an error while starting the app (please note that it says "response field" in the error, instead of "request field", however it is not the main problem):
FastAPI: 0.100.1 |
Beta Was this translation helpful? Give feedback.
-
Is it expected that This test only works with fastapi==0.100.1 when removing My expectation is that it still works as you state "This version of FastAPI still supports Pydantic v1.". import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from pydantic.v1 import BaseModel
app = FastAPI()
pytestmark = pytest.mark.anyio
class MyResponse(BaseModel):
...
@pytest.fixture
def anyio_backend():
return 'asyncio'
@app.get("/", response_model=MyResponse)
async def get():
return MyResponse()
async def test():
async with AsyncClient(app=app, base_url="http://local") as client:
res = await client.get("/")
assert res.json() == {} |
Beta Was this translation helpful? Give feedback.
-
The error message shown by a model validation error is worse in FastAPI 0.100.0 and above. For the following test code, the route will raise a from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class RootSchema(BaseModel):
foo: str
@app.get("/", response_model=RootSchema)
def get_root():
return {} In
which is helpful as the error from pydantic is shown and can be used to debug the issue. However, in
which doesn't show the error from Pydantic, and makes the error quite a bit harder to debug (in my opinion). |
Beta Was this translation helpful? Give feedback.
-
Thanks all for the feedback! 🍪 ☕ As FastAPI with support for Pydantic v2 is now released, if you have any problems with it, please create a new discussion question following the template. I'm gonna close this discussion now to clean things up, there are too many conversations here and many are already solved, so it's hard to keep track of what still needs attention. So, let's continue with the normal workflow of creating a new discussion question with a minimal reproducible example, following all the steps, etc. 🤓 |
Beta Was this translation helpful? Give feedback.
Thanks all for the feedback! 🍪 ☕
As FastAPI with support for Pydantic v2 is now released, if you have any problems with it, please create a new discussion question following the template. I'm gonna close this discussion now to clean things up, there are too many conversations here and many are already solved, so it's hard to keep track of what still needs attention. So, let's continue with the normal workflow of creating a new discussion question with a minimal reproducible example, following all the steps, etc. 🤓