Pydantic es una librería de Python que permite definir modelos de datos usando clases. Su objetivo principal es validar y convertir datos de forma automática. Está basada en type hints (sugerencia de tipos en Python).
type hints (documentación oficial)
FastAPI la integra de manera nativa:
/docs
).Imaginemos que tu API recibe información de un usuario. En lugar de validar campo por campo manualmente, definís un modelo Pydantic que describe la estructura esperada, y Pydantic se encarga de todo.
/docs
.from pydantic import BaseModel
class Usuario(BaseModel):
id: int
nombre: str
email: str
activo: bool = True # Valor por defecto
id
debe ser un número entero.nombre
debe ser un texto.email
debe ser un texto.activo
es un booleano, y si no se envía, por defecto es True
.usuario1 = Usuario(id=1, nombre="Diego", email="diego@test.com")
print(usuario1)
# Salida:
# id=1 nombre='Diego' email='diego@test.com' activo=True
# Si enviamos datos incorrectos:
usuario2 = Usuario(id="uno", nombre="Diego", email="diego@test.com")
# Error:
# pydantic.error_wrappers.ValidationError: 1 validation error for Usuario
# id
# value is not a valid integer (type=type_error.integer)
Pydantic puede convertir datos si son compatibles:
usuario1 = Usuario(id="2", nombre="María", email="maria@test.com")
print(usuario1.id, type(uusuario1.id)) # 2 <class 'int'>
Podemos definir campos opcionales con None
y dar valores iniciales:
from typing import Optional
class Usuario(BaseModel):
id: int
nombre: str
email: Optional[str] = None
activo: bool = True
Aquí:
email
puede omitirse: por defecto será None
.activo
será True
si no se envía.Field
permite agregar restricciones, descripciones y ejemplos.
from pydantic import BaseModel, Field
class Producto(BaseModel):
codigo: int = Field(gt=0, description="Debe ser un número positivo")
descripcion: str = Field(min_length=3, max_length=100)
precio: float = Field(gt=0, description="Precio mayor que 0", example=150.0)
Validaciones:
gt=0
: el código y precio deben ser mayores que 0.min_length
/ max_length
: controlan la longitud de texto.example
: se muestra en la documentación automática de FastAPI.p = Producto(codigo=1, descripcion="Teclado", precio=50.0)
# Convertir a diccionario
print(p.dict())
# Convertir a JSON
print(p.json())
# Acceder como atributos
print(p.codigo, p.descripcion)
# Salida (ejemplo):
# {"codigo": 1, "descripcion": "Teclado", "precio": 50.0}
Archivo main.py
:
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Producto(BaseModel):
codigo: int = Field(gt=0, description="Número positivo")
descripcion: str = Field(min_length=3, max_length=100)
precio: float = Field(gt=0, description="Precio mayor que 0")
@app.post("/productos/")
def crear_producto(producto: Producto):
return {
"mensaje": "Producto creado correctamente",
"datos": producto
}
Probar en Swagger UI (/docs
):
Enviar este JSON en POST /productos/:
{
"codigo": 1,
"descripcion": "Monitor",
"precio": 250.5
}
# Respuesta:
{
"mensaje": "Producto creado correctamente",
"datos": {
"codigo": 1,
"descripcion": "Monitor",
"precio": 250.5
}
}
Si enviamos un JSON inválido:
{
"codigo": -5,
"descripcion": "A",
"precio": -10
}
# FastAPI responde automáticamente con:
{
"detail": [
{"loc": ["body","codigo"], "msg": "ensure this value is greater than 0", "type": "value_error.number.not_gt"},
{"loc": ["body","descripcion"], "msg": "ensure this value has at least 3 characters", "type": "value_error.any_str.min_length"},
{"loc": ["body","precio"], "msg": "ensure this value is greater than 0", "type": "value_error.number.not_gt"}
]
}
# No hace falta escribir validaciones manuales!