With the release of Pydantic V2, a series of changes and improvements have been introduced to make the library more robust, faster, and easier to use. For those of us working with FastAPI, which relies heavily on Pydantic for data validation, this update implies the need to adapt our code. In this post, we will review the most common changes you will encounter when migrating a FastAPI application that uses Pydantic V1 to V2.
Main Changes
1. Config is now ConfigDict
One of the most immediate changes you will notice is that the internal Config class used to configure model behavior has been renamed to ConfigDict. Additionally, it is now recommended to be a dictionary rather than a class.
# Pydantic V1
class Task(BaseModel):
# ...
class Config:
orm_mode = True # Pydantic V2
from pydantic import ConfigDict
class Task(BaseModel):
model_config = ConfigDict(from_attributes=True)
# ...The error you will see if you don't make this change is:
* config is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.02. orm_mode is now from_attributes
Related to the previous point, the orm_mode attribute that allowed Pydantic models to read data directly from ORM models (such as SQLAlchemy) has been renamed to from_attributes to make its purpose clearer.
# Pydantic V1
class Task(BaseModel):
class Config:
orm_mode = True
# Pydantic V2
from pydantic import ConfigDict
class Task(BaseModel):
model_config = ConfigDict(from_attributes=True)The error you will see if you don't make this change is:
* 'orm_mode' has been renamed to 'from_attributes'3. schema_extra is now json_schema_extra
If you customized the OpenAPI schema generated by FastAPI to add examples, you would have used schema_extra. In Pydantic V2, this has been renamed to json_schema_extra.
# Pydantic V1
class Task(BaseModel):
class Config:
schema_extra = {
"example": {
"id": 123,
"name": "Save the world",
}
}
# Pydantic V2
from pydantic import ConfigDict
class Task(BaseModel):
model_config = ConfigDict(
json_schema_extra={
"example": {
"id": 123,
"name": "Save the world",
}
}
)
The error you will see if you don't make this change is:
* 'schema_extra' has been renamed to 'json_schema_extra'4. SQLAlchemy Update: declarative_base
Although not a direct Pydantic change, it is common that when updating your dependencies, you also update SQLAlchemy. In recent versions of SQLAlchemy, declarative_base() has moved to sqlalchemy.orm.
# Older versions of SQLAlchemy
from sqlalchemy.ext.declarative import declarative_base
# SQLAlchemy 2.0 and above
from sqlalchemy.orm import declarative_base
The warning you will see if you use the old version is:
* MovedIn20Warning: The declarative_base() function is now available as sqlalchemy.orm.declarative_base(). (deprecated since: 2.0)5. @validator is now @field_validator
Decorators for custom validations have changed. The old @validator has been replaced by @field_validator, which offers a clearer and more explicit syntax.
# Pydantic V1
from pydantic import BaseModel, validator
class Task(BaseModel):
id: int
@validator('id')
def greater_than_zero(cls, v):
if v <= 0:
raise ValueError('must be greater than zero')
return v
# Pydantic V2
from pydantic import BaseModel, field_validator
class Task(BaseModel):
id: int
@field_validator('id')
def greater_than_zero(cls, v):
if v <= 0:
raise ValueError('must be greater than zero')
return vThe warning you will see if you use the old version is:
* @validator validators are deprecated. You should migrate to Pydantic V2 style @field_validator validators...6. regex in Query and Path is now pattern
When using regular expressions for validation in Query or Path parameters in FastAPI, the regex parameter has been deprecated in favor of pattern.
# Pydantic V1
from fastapi import FastAPI, Path
@app.get("/ep_phone/{phone}")
def phone(phone: str = Path(regex=r"^\d+$")):
return {"phone": phone}
# Pydantic V2
from fastapi import FastAPI, Path
@app.get("/ep_phone/{phone}")
def phone(phone: str = Path(pattern=r"^\d+$")):
return {"phone": phone}The warning you will see if you use the old version is:
* DeprecationWarning: regex has been deprecated, please use pattern instead* @validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.0.3/migration/For regular expressions, it is no longer regex but pattern:
* DeprecationWarning: `regex` has been depreacated, please use `pattern` instead
def phone(phone: Annotated[str, Query(regex=r"^(\(?\+[\d]{1,3}\)?)\s?([\d]{1,5})\s?([\d][\s\.-]?){6,7}$", example="+34 111 12-34-56")]):@app.get("/ep_phone/{phone}") # +34 111 12-34-56
def phone(phone: str = Path(regex=r"^(\(?\+[\d]{1,3}\)?)\s?([\d]{1,5})\s?([\d][\s\.-]?){6,7}$", example="+34 111 12-34-56")):
return {"phone": phone}Now it is:
@app.get("/ep_phone/{phone}") # +34 111 12-34-56
def phone(phone: str = Path(pattern=r"^(\(?\+[\d]{1,3}\)?)\s?([\d]{1,5})\s?([\d][\s\.-]?){6,7}$", example="+34 111 12-34-56")):
return {"phone": phone}These are some of the changes made in the FastAPI course and book at the project level.
Conclusion
Migrating to Pydantic V2 requires a series of changes in our code, but the performance improvements and the clarity of the new API make the effort worthwhile. We hope this guide is useful for updating your FastAPI applications.