Pydantic y validación
¿Qué es?
Pydantic es una librería de Python para definir la forma que deben tener tus datos y comprobar que la cumplen. Validar significa revisar que lo que llega es correcto antes de usarlo: que un número esté en su rango, que un texto no esté vacío, que un correo tenga forma de correo. Defines esa forma como una clase (un «modelo») con tipos. FastAPI usa Pydantic para no fiarse de nada de lo que envía un cliente.
¿Cómo funciona?
Creas una clase que hereda de BaseModel y le declaras atributos con sus tipos; con Field añades reglas más finas (mínimos, máximos, longitudes). Cuando declaras ese modelo como argumento de un endpoint, FastAPI lee el cuerpo JSON de la petición, comprueba que cumple el modelo y te entrega un objeto ya construido y validado. Si algo no cuadra, responde solo un error 422 señalando el campo exacto, sin que escribas ni un if. Con response_model aplicas el mismo control a lo que sale.
¿Para qué sirve?
Validar evita que datos basura o maliciosos entren a tu sistema, y documenta de paso qué espera cada endpoint. Definir las reglas una sola vez en el modelo te ahorra repetirlas y mantenerlas por todo el código. En el proyecto final, «API para tus herramientas», los modelos darán robustez: una nota nunca pasará de 5.0 y una hoja OMR tendrá siempre la estructura esperada, porque el modelo lo garantiza antes de tocar nada.
Hasta ahora recibimos datos sueltos (path y query). Para los cuerpos de petición —el JSON que envía un cliente en un POST— necesitamos modelos. FastAPI usa Pydantic v2: defines una clase con tipos y Pydantic valida, convierte y documenta los datos por ti. Esto es lo que vuelve a FastAPI tan productivo y lo que dará robustez al proyecto «API para tus herramientas».
Tu primer modelo
Un modelo es una clase que hereda de BaseModel:
from pydantic import BaseModel
class Nota(BaseModel):
estudiante: str
valor: float
materia: str = "General"
Cada atributo tiene un tipo. materia tiene valor por defecto, así que es opcional. Para usarlo como cuerpo de petición, lo declaras como argumento del endpoint:
from fastapi import FastAPI
app = FastAPI()
@app.post("/notas")
def crear_nota(nota: Nota):
return {"recibida": nota, "aprobada": nota.valor >= 3.0}
FastAPI ve que nota es un modelo Pydantic y deduce que viene en el cuerpo JSON. Lee el body, valida los tipos y te entrega un objeto Nota ya construido. Si el cliente envía valor: "hola", recibe un 422 con la ubicación exacta del error, sin código tuyo.
Validación de campos
Para reglas más finas usa Field:
from pydantic import BaseModel, Field
class Nota(BaseModel):
estudiante: str = Field(min_length=1, max_length=80)
valor: float = Field(ge=0.0, le=5.0) # ge = mayor o igual, le = menor o igual
materia: str = "General"
ge/le/gt/lt acotan números; min_length/max_length acotan textos. Si llega valor: 9, Pydantic rechaza con un mensaje que dice que debe ser <= 5. Tipos especializados como EmailStr (requiere pip install "pydantic[email]") validan formatos comunes:
from pydantic import EmailStr
class Estudiante(BaseModel):
nombre: str
correo: EmailStr
edad: int = Field(ge=0, le=120)
Mete las reglas de negocio en el modelo, no en el endpoint. Si una nota nunca puede pasar de 5.0, eso es una propiedad del dato y pertenece al Field. Así la regla queda documentada en /docs automáticamente.
response_model: controlar lo que sale
Igual que validas la entrada, conviene controlar la salida. response_model filtra y documenta la respuesta:
class NotaEntrada(BaseModel):
estudiante: str
valor: float = Field(ge=0, le=5)
class NotaSalida(BaseModel):
estudiante: str
valor: float
aprobada: bool
@app.post("/notas", response_model=NotaSalida)
def crear_nota(nota: NotaEntrada):
return {
"estudiante": nota.estudiante,
"valor": nota.valor,
"aprobada": nota.valor >= 3.0,
}
response_model garantiza que la respuesta tenga exactamente esa forma. Cualquier campo extra que devuelvas se elimina. Esto es clave para seguridad: si tu modelo interno tuviera un password_hash, un response_model sin ese campo impide que se filtre.
Modelos anidados
Los modelos se componen. Un campo puede ser otro modelo o una lista de modelos:
class Respuesta(BaseModel):
pregunta: int
marcada: str
class HojaOMR(BaseModel):
estudiante: str
respuestas: list[Respuesta]
Pydantic valida toda la estructura en profundidad. Esto modela bien una hoja de respuestas OMR del proyecto final.
Ejemplo guiado: registrar y validar notas
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI(title="API de notas")
NOTAS: list[dict] = []
class NotaEntrada(BaseModel):
estudiante: str = Field(min_length=1)
valor: float = Field(ge=0.0, le=5.0)
class NotaSalida(BaseModel):
id: int
estudiante: str
valor: float
aprobada: bool
@app.post("/notas", response_model=NotaSalida, status_code=201)
def crear_nota(nota: NotaEntrada):
registro = {
"id": len(NOTAS) + 1,
"estudiante": nota.estudiante,
"valor": nota.valor,
"aprobada": nota.valor >= 3.0,
}
NOTAS.append(registro)
return registro
Pasos para probarlo:
- Corre
fastapi dev main.pyy abre/docs. - En
POST /notaspulsa Try it out. El editor ya muestra un ejemplo del body gracias al modelo. - Envía
{"estudiante": "Ana", "valor": 4.5}: responde 201 conaprobada: true. - Envía
{"estudiante": "", "valor": 9}: responde 422 con dos errores (texto vacío y valor fuera de rango). Lee cómo Pydantic señala el campo exacto.
status_code=201 comunica «creado», la respuesta correcta para un POST que crea un recurso.
En Pydantic v2 la configuración cambió respecto a v1. Para opciones del modelo usa model_config = ConfigDict(...) en lugar de la clase interna class Config. Y los validadores se decoran con @field_validator, no con el viejo @validator. Mezclar sintaxis de v1 y v2 es el error más común al migrar.
Tech English: request body = cuerpo de la petición. schema = esquema (la forma de los datos). serialize = convertir el objeto a JSON; validate = comprobar que cumple el esquema.
Ejercicios
-
Inscripción de estudiantes. Diseña
POST /estudiantescon un modelo que validenombre(no vacío),correo(EmailStr) yedad(entre 5 y 100). Usa unresponse_modelque NO devuelva el correo. Evalúa: ¿las restricciones están en el modelo? ¿el 422 es informativo? ¿la salida oculta el dato sensible? -
Hoja OMR. Modela
POST /hojasque reciba unaHojaOMR(estudiante + lista de respuestas, cada una con número de pregunta y letra marcada de la A a la E). Restringe la letra marcada a valores válidos. Piensa cómo el modelo anidado documenta solo la estructura esperada en/docs. Esta es la entrada del calificador OMR del proyecto final.