Juan Diego Andrés PRADA··RAMÍREZ Entrar
Lección 3 de 7

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:

  1. Corre fastapi dev main.py y abre /docs.
  2. En POST /notas pulsa Try it out. El editor ya muestra un ejemplo del body gracias al modelo.
  3. Envía {"estudiante": "Ana", "valor": 4.5}: responde 201 con aprobada: true.
  4. 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

  1. Inscripción de estudiantes. Diseña POST /estudiantes con un modelo que valide nombre (no vacío), correo (EmailStr) y edad (entre 5 y 100). Usa un response_model que NO devuelva el correo. Evalúa: ¿las restricciones están en el modelo? ¿el 422 es informativo? ¿la salida oculta el dato sensible?

  2. Hoja OMR. Modela POST /hojas que reciba una HojaOMR (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.

Tu progreso se guarda en este navegador. Inicia sesión para guardarlo en tu cuenta y verlo desde cualquier dispositivo.