Con el lanzamiento de Pydantic V2, se han introducido una serie de cambios y mejoras que buscan hacer la librería más robusta, rápida y fácil de usar. Para aquellos que trabajamos con FastAPI, que depende en gran medida de Pydantic para la validación de datos, esta actualización implica la necesidad de adaptar nuestro código. En esta entrada, repasaremos los cambios más comunes que encontrarás al migrar una aplicación de FastAPI que utiliza Pydantic V1 a la V2.
Cambios Principales
1. Config ahora es ConfigDict
Uno de los cambios más inmediatos que notarás es que la clase interna Config utilizada para configurar el comportamiento de los modelos ha sido renombrada a ConfigDict. Además, ahora se recomienda que sea un diccionario y no una clase.
# 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)
# ...El error que verás si no haces este cambio es:
* config is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.02. orm_mode ahora es from_attributes
Relacionado con el punto anterior, el atributo orm_mode que permitía a los modelos de Pydantic leer datos directamente de modelos de ORM (como los de SQLAlchemy) ha sido renombrado a from_attributes para que su propósito sea más claro.
# 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)El error que verás si no haces este cambio es:
* 'orm_mode' has been renamed to 'from_attributes'3. schema_extra ahora es json_schema_extra
Si personalizabas el esquema de OpenAPI generado por FastAPI para añadir ejemplos, habrás usado schema_extra. En Pydantic V2, este ha sido renombrado a json_schema_extra.
# Pydantic V1
class Task(BaseModel):
class Config:
schema_extra = {
"example": {
"id": 123,
"name": "Salvar al mundo",
}
}
# Pydantic V2
from pydantic import ConfigDict
class Task(BaseModel):
model_config = ConfigDict(
json_schema_extra={
"example": {
"id": 123,
"name": "Salvar al mundo",
}
}
)
El error que verás si no haces este cambio es:
* 'schema_extra' has been renamed to 'json_schema_extra'4. Actualización en SQLAlchemy: declarative_base
Aunque no es un cambio directo de Pydantic, es común que al actualizar tus dependencias, también actualices SQLAlchemy. En versiones recientes de SQLAlchemy, declarative_base() se ha movido a sqlalchemy.orm.
# Versiones anteriores de SQLAlchemy
from sqlalchemy.ext.declarative import declarative_base
# SQLAlchemy 2.0 y superior
from sqlalchemy.orm import declarative_base
El warning que verás si usas la versión antigua es:
* MovedIn20Warning: The declarative_base() function is now available as sqlalchemy.orm.declarative_base(). (deprecated since: 2.0)5. @validator ahora es @field_validator
Los decoradores para validaciones personalizadas han cambiado. El antiguo @validator ha sido reemplazado por @field_validator, que ofrece una sintaxis más clara y explícita.
# 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 vEl warning que verás si usas la versión antigua es:
* @validator validators are deprecated. You should migrate to Pydantic V2 style @field_validator validators...6. regex en Query y Path ahora es pattern
Cuando se usan expresiones regulares para validación en parámetros de Query o Path en FastAPI, el parámetro regex ha sido deprecado en favor de 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}El warning que verás si usas la versión antigua es:
* 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/Para las expresiones regulares ya no es regex si no 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}Ahora es:
@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}Estos son algunos de los cambios realizados en el curso y libro sobre FastAPI a nivel del proyecto.
Conclusión
La migración a Pydantic V2 requiere de una serie de cambios en nuestro código, pero las mejoras en rendimiento y la claridad de la nueva API hacen que el esfuerzo valga la pena. Esperamos que esta guía te sea de utilidad para actualizar tus aplicaciones de FastAPI.