Saltar al contenido principal

Pydantic: Una guía con ejemplos prácticos

Aprende qué es Pydantic, para qué sirve y cómo se compara con alternativas como Marshmallow o las clases de datos de Python.
Actualizado 25 jun 2025  · 12 min de lectura

Pydantic es la biblioteca de validación de datos más popular de Python, que puede convertir sugerencias de tipo en reglas de validación en tiempo de ejecución.

En lugar de escribir docenas de si isinstance() y funciones de validación personalizadas, define tu estructura de datos una sola vez utilizando la conocida sintaxis de Python.

Pydantic se encarga del resto: valida los datos entrantes, convierte los tipos cuando es necesario y proporciona mensajes de error claros cuando falla la validación.

Considera esta sencilla función de Python con sugerencias de tipo y la documentación adecuada:

def calculate_user_discount(age: int, is_premium_member: bool, purchase_amount: float) -> float:
   """Calculate discount percentage based on user profile and purchase amount."""
   if age >= 65:
       base_discount = 0.15
   elif is_premium_member:
       base_discount = 0.10
   else:
       base_discount = 0.05
  
   return purchase_amount * base_discount

Si observas su firma, sabrás exactamente qué tipo de datos acepta y produce en el otro extremo. Sin embargo, ¿puedes decir lo mismo del usuario final? Al fin y al cabo, estas indicaciones de tipo sólo sirven para documentar: si alguien pasa un tipo de dato incorrecto, el intérprete de Python nunca se molesta:

# This runs without error, even though types are completely wrong!
discount = calculate_user_discount(True, 1, 5)
print(discount)  # Output: 0.5 (True becomes 1, 1 is truthy, so 5 * 0.10)

Este es un ejemplo clásico de la tipificación dinámica de Python. Ofrece a los programadores un desarrollo rápido y libertad, pero a costa de introducir problemas de validación de tipos que a menudo surgen en producción.

En este tutorial, te explicaré cómo utilizar Pydantic para aplicaciones del mundo real. Empezaremos con tu primer modelo de datos y avanzaremos a través de patrones avanzados utilizados en sistemas de producción. Descubrirás cómo validar estructuras de datos anidadas, escribir validadores personalizados para reglas de negocio complejas e integrar Pydantic con marcos populares como FastAPI y SQLAlchemy.

¿Qué es Pydantic?

Básicamente estás jugando a la ruleta de los datos cuando creas aplicaciones que interactúan con el mundo exterior. Los usuarios pueden enviar un número de teléfono como una cadena en lugar de un número entero, una API puede devolver valores nulos donde esperas números, o un archivo de configuración puede contener errores tipográficos que rompan tu aplicación a las 3 de la madrugada.

Los enfoques tradicionales de Python a este problema se vuelven rápidamente difíciles de manejar. Acabas escribiendo un código de validación como éste:

def create_user(data):
   # Manual validation nightmare
   if not isinstance(data.get('age'), int):
       raise ValueError("Age must be an integer")
   if data['age'] < 0 or data['age'] > 150:
       raise ValueError("Age must be between 0 and 150")
   if not isinstance(data.get('email'), str) or '@' not in data['email']:
       raise ValueError("Invalid email format")
   if not isinstance(data.get('is_active'), bool):
       raise ValueError("is_active must be a boolean")
  
   # Finally create the user...
   return User(data['age'], data['email'], data['is_active'])

Multiplica esto por cada estructura de datos de tu aplicación, y pasarás más tiempo escribiendo código de validación que lógica empresarial.

Pydantic lo resuelve combinando tres potentes conceptos: sugerencias de tipo, validación en tiempo de ejecución y serialización automática. En lugar de comprobaciones manuales, defines tu estructura de datos una vez utilizando la sintaxis de anotación de tipos de Python, y Pydantic se encarga de toda la validación automáticamente:

from pydantic import BaseModel, EmailStr
from typing import Optional

class User(BaseModel):
   age: int
   email: EmailStr
   is_active: bool = True
   nickname: Optional[str] = None

# Pydantic automatically validates and converts data
user_data = {
   "age": "25",  # String gets converted to int
   "email": "john@example.com",
   "is_active": "true"  # String gets converted to bool
}

user = User(**user_data)
print(user.age)  # 25 (as integer)
print(user.model_dump())  # Clean dictionary output

Características de Pydantic

El enfoque de Pydantic te ofrece varias ventajas. El rendimiento es la primera ventaja que notarás: la lógica central de validación de Pydantic está escrita en Rust, lo que la hace más rápida que la validación escrita a mano en Python en la mayoría de los casos. Tu aplicación puede procesar miles de peticiones sin que la validación se convierta en un cuello de botella.

Pydantic también permite la integración con frameworks modernos. FastAPI, uno de los frameworks web de Python de más rápido crecimiento, utiliza modelos Pydantic para generar automáticamente la documentación de la API, validar los cuerpos de las peticiones y serializar las respuestas. Cuando defines un modelo Pydantic, obtienes gratis la generación de esquemas OpenAPI:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class UserCreate(BaseModel):
   name: str
   email: EmailStr
   age: int

@app.post("/users/")
async def create_user(user: UserCreate):
   # FastAPI automatically validates the request body
   # and generates API docs from your Pydantic model
   return {"message": f"Created user {user.name}"}

La generación del esquema JSON se produce automáticamente con cada modelo Pydantic. Esto significa que tus estructuras de datos se autodocumentan, y puedes generar bibliotecas de clientes, reglas de validación para aplicaciones frontales o esquemas de bases de datos a partir de la misma fuente de verdad.

Pydantic contra Clases de datos vs. Malvavisco

Pero, ¿cuándo debes elegir Pydantic frente a otras alternativas? Si estás comparando Pydantic frente a clases de datos, la decisión se reduce a las necesidades de validación. @dataclass de Python es perfecto para contenedores de datos sencillos en los que confías en la entrada, pero Pydantic sobresale cuando necesitas validación, serialización e integración con marcos web:

from dataclasses import dataclass
from pydantic import BaseModel

# Dataclass: fast, simple, no validation
@dataclass
class UserDataclass:
   name: str
   age: int

# Pydantic: validation, serialization, framework integration
class UserPydantic(BaseModel):
   name: str
   age: int

Al comparar Pydantic con Marshmallow, el enfoque de sugerencias de tipo de Pydantic resulta más natural para los programadores de Python, mientras que Marshmallow utiliza clases de esquema. Pydantic también ofrece un mejor rendimiento y una integración más sencilla con los marcos asíncronos modernos.

Pydantic funciona mejor cuando estás construyendo APIs, procesando datos externos, gestionando la configuración, o cualquier escenario en el que un fallo en la validación de datos deba detectarse pronto en lugar de causar misteriosos errores más tarde. Convierte los errores de ejecución en mensajes de validación claros y procesables que ayudan tanto a programadores como a usuarios a entender qué ha fallado.

Primeros pasos con Pydantic

Vamos a sumergirnos en cómo utilizar Pydantic en tus proyectos, desde la instalación hasta la creación de tus primeros modelos de datos.

Instalación y configuración

Configurar Pydantic es sencillo, pero algunos errores comunes pueden hacer tropezar a los recién llegados. Te guiaré por el proceso de instalación adecuado y te ayudaré a evitar los errores que provocan depuraciones innecesarias.

Primero, crea un entorno virtual para mantener aisladas las dependencias de tu proyecto. Así se evitan conflictos de versión que pueden causar misteriosos errores de importación más adelante:

# Create and activate virtual environment (works in zsh and bash)
python -m venv pydantic_env
source pydantic_env/bin/activate  # On macOS/Linux
# pydantic_env\Scripts\activate  # On Windows

Ahora, instala Pydantic con el siguiente comando:

pip install pydantic

Para la funcionalidad de validación del correo electrónico, que probablemente necesitarás en aplicaciones reales (y en este tutorial), instala los extras de correo electrónico:

pip install "pydantic[email]"

Las comillas alrededor de pydantic[email] son importantes en casi todos los terminales. Sin ellos, es posible que aparezca un error "no se han encontrado coincidencias".

Nota: Si te encuentras con ModuleNotFoundError: No module named 'pydantic', comprueba estas cuestiones:

  1. Entorno de Python incorrecto: Asegúrate de que tu entorno virtual está activado
  2. Múltiples versiones de Python: Comprueba que utilizas la misma versión de Python que instaló Pydantic
  3. Configuración IDE: Puede que tu editor de código esté utilizando un intérprete de Python diferente

Advertencia de denominación crítica: Nunca nombres tu archivo Python pydantic.py. Esto crea una importación circular que romperá tu código con mensajes de error confusos. Python intentará importar tu archivo en lugar de la biblioteca Pydantic real.

Tu primer modelo pydántico

Construyamos un modelo de usuario sencillo para ver Pydantic en acción. En lugar de empezar con ejemplos abstractos, resolveremos un problema real: validar los datos de registro de un usuario desde un formulario web.

from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import datetime

class User(BaseModel):
   name: str
   email: EmailStr
   age: int
   is_active: bool = True
   created_at: datetime = None

# Test with clean data
clean_data = {
   "name": "Alice Johnson",
   "email": "alice@example.com",
   "age": 28
}

user = User(**clean_data)
print(f"User created: {user.name}, Age: {user.age}")
print(f"Model output: {user.model_dump()}")

Salida:

User created: Alice Johnson, Age: 28
Model output: {'name': 'Alice Johnson', 'email': 'alice@example.com', 'age': 28, 'is_active': True, 'created_at': None}

Vamos a desglosar lo que ocurre en esta definición del modelo:

  • Crear un modelo pydántico: Tu clase User hereda de BaseModel, lo que le proporciona todas las capacidades de validación y serialización de Pydantic. Esta herencia convierte una clase normal de Python en una herramienta de validación de datos.
  • Definiciones de los campos: Cada línea de la clase define un campo con su tipo previsto. La sintaxis name: str indica a Pydantic que name debe ser una cadena, age: int significa que la edad debe ser un número entero, y así sucesivamente.
  • EmailStr explicado: EmailStr es un tipo especial de Pydantic que valida automáticamente las direcciones de correo electrónico. Proviene del paquete pydantic[email] que instalaste anteriormente y utiliza expresiones regulares para garantizar que el formato del correo electrónico es válido. Si alguien pasa "no-un-email", Pydantic emitirá un error de validación.
  • Valores por defecto: Los campos como is_active: bool = True tienen valores por defecto. Si no proporcionas estos campos al crear un usuario, Pydantic utiliza los predeterminados. En = None para created_at este campo es opcional.
  • Instanciación del modelo: Cuando llamas a User(**clean_data), ** desempaqueta tu diccionario y pasa cada par clave-valor como argumentos de palabra clave al constructor del modelo.

Veamos ahora en acción la conversión automática de tipos de Pydantic:

# Messy data that still works
messy_data = {
   "name": "Bob Smith",
   "email": "bob@company.com",
   "age": "35",  # String instead of int
   "is_active": "true"  # String instead of bool
}

user = User(**messy_data)
print(f"Age type: {type(user.age)}")  # <class 'int'>
print(f"Is active type: {type(user.is_active)}")  # <class 'bool'>

Veamos ahora en acción la conversión automática de tipos de Pydantic:

# Messy data that still works
messy_data = {
   "name": "Bob Smith",
   "email": "bob@company.com",
   "age": "35",  # String instead of int
   "is_active": "true"  # String instead of bool
}

user = User(**messy_data)
print(f"Age type: {type(user.age)}")  # <class 'int'>
print(f"Is active type: {type(user.is_active)}")  # <class 'bool'>

Salida:

Age type: <class 'int'>

Is active type: <class 'bool'>

Como puedes ver, los campos age y is_inactive se convierten automáticamente a sus formatos adecuados mediante validación.

Cuando falla la validación, Pydantic proporciona mensajes de error claros:

from pydantic import ValidationError

try:
   invalid_user = User(
       name="",  # Empty string
       email="not-an-email",  # Invalid email
       age=-5  # Negative age
   )
except ValidationError as e:
   print(e)

Salida:

   # Shows exactly which fields failed and why
1 validation error for User
email
 value is not a valid email address: An email address must have an @-sign. [type=value_error, input_value='not-an-email', input_type=str]

BaseModel vs. clases de datos

Entender cuándo utilizar la BaseModel de Pydantic frente a la @dataclass de Python te ayuda a elegir la herramienta adecuada para cada situación.

Las clases de datos de Python son perfectas para contenedores de datos sencillos en los que tú controlas la entrada:

from dataclasses import dataclass

@dataclass
class ProductDataclass:
   name: str
   price: float
   in_stock: bool

# Fast, simple, but no validation
product = ProductDataclass("Laptop", 999.99, True)

# This also works, even though types are wrong:
broken_product = ProductDataclass(123, "expensive", "maybe")

Los modelos Pydantic añaden validación, serialización e integración de marcos:

from pydantic import BaseModel, Field

class ProductPydantic(BaseModel):
   name: str = Field(min_length=1)
   price: float = Field(gt=0)  # Must be greater than 0
   in_stock: bool

# Automatic validation prevents bad data
try:
   product = ProductPydantic(name="", price=-10, in_stock="maybe")
except ValidationError as e:
   print("Validation caught the errors!")

# Valid data works perfectly
good_product = ProductPydantic(
   name="Laptop",
   price="999.99",  # String converted to float
   in_stock=True
)

Cuándo elegir cada enfoque:

  • Utiliza clases de datos para estructuras de datos internas, objetos de configuración, o cuando el rendimiento sea crítico y confíes en tus fuentes de datos
  • Utiliza Pydantic para puntos finales de API, entradas de usuario, análisis de datos externos o cuando necesites serialización JSON

Pydantic añade cierta sobrecarga en comparación con las clases de datos, pero este coste suele ser insignificante comparado con los errores que evita y el tiempo de desarrollo que ahorra. Para las aplicaciones web, la integración automática con frameworks como FastAPI hace que Pydantic sea la elección clara.

Las funciones de validación y serialización adquieren más valor a medida que crece tu aplicación. Empezar con modelos Pydantic te proporciona una base sólida que se adapta a tus necesidades.

Construir modelos de datos con Pydantic

Ahora que ya conoces los conceptos básicos, abordemos los retos a los que te enfrentarás al crear aplicaciones listas para la producción.

Validación y limitaciones del campo

Considera una API de catálogo de productos en la que los datos de precios proceden de varios proveedores con diferentes normas de formato. Algunos envían los precios como cadenas, otros como flotantes y, ocasionalmente, alguien envía un precio negativo que bloquea tu sistema de facturación.

La función Field() de Pydantic transforma las sugerencias de tipo básicas en sofisticadas reglas de validación que protegen tu aplicación:

from pydantic import BaseModel, Field
from decimal import Decimal
from typing import Optional

class Product(BaseModel):
   name: str = Field(min_length=1, max_length=100)
   price: Decimal = Field(gt=0, le=10000)  # Greater than 0, less than or equal to 10,000
   description: Optional[str] = Field(None, max_length=500)
   category: str = Field(..., pattern=r'^[A-Za-z\s]+$')  # Only letters and spaces
   stock_quantity: int = Field(ge=0)  # Greater than or equal to 0
   is_available: bool = True

# This works - all constraints satisfied
valid_product = Product(
   name="Wireless Headphones",
   price="199.99",  # String converted to Decimal
   description="High-quality wireless headphones",
   category="Electronics",
   stock_quantity=50
)

# This fails with clear error messages
try:
   invalid_product = Product(
       name="",  # Too short
       price=-50,  # Negative price
       category="Electronics123",  # Contains numbers
       stock_quantity=-5  # Negative stock
   )
except ValidationError as e:
   print(f"Validation errors: {len(e.errors())} issues found")

Cada parámetro de Field() tiene una finalidad específica: min_length y max_length evitan las violaciones del esquema de la base de datos, gt y le crean límites de lógica empresarial, y pattern valida los datos formateados mediante expresiones regulares. La sintaxis Field(...) con elipsis marca los campos obligatorios, mientras que Field(None, ...) crea campos opcionales con reglas de validación.

Coerción de tipos frente a validación estricta

Por defecto, Pydantic convierte los tipos compatibles en lugar de rechazarlos de plano. Esta flexibilidad funciona bien para las entradas del usuario, pero algunos escenarios exigen una correspondencia exacta de los tipos:

from pydantic import BaseModel, Field, ValidationError

# Default: lenient type coercion
class FlexibleOrder(BaseModel):
   order_id: int
   total_amount: float
   is_paid: bool

# These all work due to automatic conversion
flexible_order = FlexibleOrder(
   order_id="12345",  # String to int
   total_amount="99.99",  # String to float
   is_paid="true"  # String to bool
)

# Strict validation when precision matters
class StrictOrder(BaseModel):
   model_config = {"str_strip_whitespace": True, "validate_assignment": True}
  
   order_id: int = Field(strict=True)
   total_amount: float = Field(strict=True)
   is_paid: bool = Field(strict=True)

El diccionario model_config controla el comportamiento de validación en todo tu modelo. La opción str_strip_whitespace limpia automáticamente la entrada de cadenas, mientras que validate_assignment garantiza que los cambios de campo posteriores a la creación del modelo sigan activando la validación. Los campos individuales pueden anular estos ajustes con Field(strict=True) para situaciones que requieran una correspondencia de tipo exacta, como los cálculos financieros o los datos científicos.

Modelos anidados y datos complejos

Las aplicaciones reales manejan estructuras de datos complejas e interconectadas. Un pedido de comercio electrónico contiene información del cliente, direcciones de envío y varios artículos de producto, cada uno de los cuales requiere su propia validación:

from typing import List
from datetime import datetime

class Address(BaseModel):
   street: str = Field(min_length=5)
   city: str = Field(min_length=2)
   postal_code: str = Field(pattern=r'^\d{5}(-\d{4})?$')
   country: str = "USA"

class Customer(BaseModel):
   name: str = Field(min_length=1)
   email: EmailStr
   shipping_address: Address
   billing_address: Optional[Address] = None

class OrderItem(BaseModel):
   product_id: int = Field(gt=0)
   quantity: int = Field(gt=0, le=100)
   unit_price: Decimal = Field(gt=0)

class Order(BaseModel):
   order_id: str = Field(pattern=r'^ORD-\d{6}$')
   customer: Customer
   items: List[OrderItem] = Field(min_items=1)
   order_date: datetime = Field(default_factory=datetime.now)

# Complex nested data validation
order_data = {
   "order_id": "ORD-123456",
   "customer": {
       "name": "John Doe",
       "email": "john@example.com",
       "shipping_address": {
           "street": "123 Main Street",
           "city": "Anytown",
           "postal_code": "12345"
       }
   },
   "items": [
       {"product_id": 1, "quantity": 2, "unit_price": "29.99"},
       {"product_id": 2, "quantity": 1, "unit_price": "149.99"}
   ]
}

order = Order(**order_data)
print(f"Order validated with {len(order.items)} items")

Pydantic valida las estructuras anidadas de forma recursiva: el campo Customer se convierte en un objeto completo Customer, que valida su propio campo Address. La sintaxis List[OrderItem] valida cada elemento de la lista como un OrderItem, mientras que Field(min_items=1) impide que los pedidos vacíos lleguen a tu sistema de inventario. Utilizando default_factory=datetime.now se crean marcas de tiempo únicas para cada instancia de pedido.

Campos opcionales y manejo de Ninguno

Diferentes operaciones necesitan diferentes requisitos de datos. La creación de usuarios exige información completa, mientras que las actualizaciones deben aceptar cambios parciales:

from typing import Optional

class UserCreate(BaseModel):
   name: str = Field(min_length=1)
   email: EmailStr
   age: int = Field(ge=13, le=120)
   phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')

class UserUpdate(BaseModel):
   name: Optional[str] = Field(None, min_length=1)
   email: Optional[EmailStr] = None
   age: Optional[int] = Field(None, ge=13, le=120)
   phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')

# PATCH request with partial data
update_data = {"name": "Jane Smith", "age": 30}
user_update = UserUpdate(**update_data)

# Serialize only provided fields
patch_data = user_update.model_dump(exclude_none=True)
print(f"Fields to update: {list(patch_data.keys())}")

Serialización convierte los objetos Pydantic en diccionarios o cadenas JSON para su almacenamiento o transmisión. El método model_dump() se encarga de esta conversión, y exclude_none=True elimina los campos no proporcionados. Este patrón funciona perfectamente para las peticiones PATCH, en las que los clientes envían sólo los campos que quieren cambiar, evitando sobrescrituras accidentales de datos en tu base de datos.

Esta base te prepara para el siguiente reto: implementar una lógica de validación personalizada que capture las reglas empresariales exclusivas de tu aplicación.

Validación personalizada e integración en el mundo real

Construir aplicaciones de producción significa manejar datos que no se ajustan a los patrones estándar de comprobación de tipos.

Piensa en un formulario de registro de usuario en el que los requisitos de contraseña varían en función de los planes de suscripción, o en una API que recibe datos de direcciones de varios países con distintos formatos de código postal. Estos escenarios requieren una lógica de validación personalizada que capture tus reglas empresariales específicas y, al mismo tiempo, se integre sin problemas con los marcos web y los sistemas de configuración.

Esta sección te muestra cómo implementar prácticos patrones de validación personalizados, integrar modelos Pydantic con FastAPI para una documentación automática de la API, y gestionar la configuración de la aplicación mediante variables de entorno utilizando el enfoque del archivo .env en el que se basan la mayoría de las aplicaciones de producción.

Validadores de campo y validación de modelos

Cuando la lógica empresarial determina la validez de los datos, el decorador @field_validator de Pydantic transforma tus funciones de validación en parte del propio modelo. Considera un sistema de registro de usuarios en el que los distintos niveles de suscripción tienen distintos requisitos de contraseña:

from pydantic import BaseModel, field_validator, Field
import re

class UserRegistration(BaseModel):
   username: str = Field(min_length=3)
   email: EmailStr
   password: str
   subscription_tier: str = Field(pattern=r'^(free|pro|enterprise)$')
  
   @field_validator('password')
   @classmethod
   def validate_password_complexity(cls, password, info):
       tier = info.data.get('subscription_tier', 'free')
      
       if len(password) < 8:
           raise ValueError('Password must be at least 8 characters')
          
       if tier == 'enterprise' and not re.search(r'[A-Z]', password):
           raise ValueError('Enterprise accounts require uppercase letters')
          
       return password

El decorador @field_validator te da acceso a otros valores de campo a través del parámetro info.data, lo que permite reglas de validación que dependen de varios campos. El validador se ejecuta después de pasar la comprobación básica de tipos, por lo que puedes suponer con seguridad que subscription_tier es uno de los valores permitidos.

Para la validación que abarca varios campos, el decorador @model_validator se ejecuta después de validar todos los campos individuales:

from datetime import datetime
from pydantic import model_validator

class EventRegistration(BaseModel):
   start_date: datetime
   end_date: datetime
   max_attendees: int = Field(gt=0)
   current_attendees: int = Field(ge=0)
  
   @model_validator(mode='after')
   def validate_event_constraints(self):
       if self.end_date <= self.start_date:
           raise ValueError('Event end date must be after start date')
          
       if self.current_attendees > self.max_attendees:
           raise ValueError('Current attendees cannot exceed maximum')
          
       return self

El parámetro mode='after' garantiza que el validador reciba una instancia del modelo totalmente construida, lo que lo hace perfecto para la lógica empresarial que requiere acceder a todos los campos validados. El validador debe devolver self para indicar que la validación se ha realizado correctamente.

Integración FastAPI

La integración de FastAPI con Pydantic crea la validación automática de peticiones y la documentación de la API. El patrón clave consiste en crear modelos separados para las distintas operaciones, lo que te permite controlar qué datos fluyen en cada dirección:

from fastapi import FastAPI
from typing import Optional
from datetime import datetime

app = FastAPI()

class UserCreate(BaseModel):
   username: str = Field(min_length=3)
   email: EmailStr
   password: str = Field(min_length=8)

class UserResponse(BaseModel):
   id: int
   username: str
   email: EmailStr
   created_at: datetime
  
@app.post("/users/", response_model=UserResponse)
async def create_user(user: UserCreate):
   # FastAPI automatically validates the request body
   new_user = {
       "id": 1,
       "username": user.username,
       "email": user.email,
       "created_at": datetime.now()
   }
   return UserResponse(**new_user)

La separación entre modelos de entrada y de salida ofrece varias ventajas. Los modelos de entrada pueden incluir reglas de validación y campos obligatorios, mientras que los modelos de salida controlan exactamente qué datos se envían a los clientes. FastAPI genera automáticamente documentación OpenAPI a partir de tus modelos Pydantic, creando documentos de API interactivos que los programadores pueden utilizar para probar los puntos finales.

Para las operaciones de actualización, puedes crear modelos en los que todos los campos sean opcionales:

class UserUpdate(BaseModel):
   username: Optional[str] = Field(None, min_length=3)
   email: Optional[EmailStr] = None

@app.patch("/users/{user_id}")
async def update_user(user_id: int, user_update: UserUpdate):
   # Only update provided fields
   update_data = user_update.model_dump(exclude_unset=True)
   # Your database update logic here
   return {"message": f"Updated user {user_id}"}

El parámetro exclude_unset=True en las operaciones PATCH garantiza que sólo actualizas los campos que se proporcionaron explícitamente, evitando sobrescrituras accidentales. Este patrón funciona perfectamente para las API REST en las que los clientes envían actualizaciones parciales.

Gestión de la configuración con variables de entorno

Las aplicaciones de producción necesitan una gestión de la configuración segura y fácil de implantar. La página BaseSettings de Pydantic, combinada con los archivos .env, proporciona una configuración segura que funciona en entornos de desarrollo, preparación y producción.

Primero, crea un archivo .env en la raíz de tu proyecto:

# .env file
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
SECRET_KEY=your-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com

A continuación, define tu modelo de configuración:

from pydantic import BaseSettings, Field
from typing import List

class AppSettings(BaseSettings):
   database_url: str = Field(description="Database connection URL")
   secret_key: str = Field(description="Secret key for JWT tokens")
   debug: bool = Field(default=False)
   allowed_hosts: List[str] = Field(default=["localhost"])
  
   class Config:
       env_file = ".env"
       case_sensitive = False

# Load settings automatically from environment and .env file
settings = AppSettings()

La clase BaseSettings lee automáticamente las variables de entorno, los archivos .env y los argumentos de la línea de comandos. Las variables de entorno tienen prioridad sobre los valores del archivo .env, lo que facilita anular la configuración en distintos entornos de despliegue. La opción case_sensitive = False permite nombrar las variables de entorno de forma flexible.

Para aplicaciones complejas, puedes organizar los ajustes en grupos lógicos:

class DatabaseSettings(BaseSettings):
   url: str = Field(env="DATABASE_URL")
   max_connections: int = Field(default=5, env="DB_MAX_CONNECTIONS")

class AppSettings(BaseSettings):
   secret_key: str
   debug: bool = False
   database: DatabaseSettings = DatabaseSettings()
  
   class Config:
       env_file = ".env"

settings = AppSettings()
# Access nested configuration
db_url = settings.database.url

Este enfoque anidado mantiene juntas las configuraciones relacionadas, al tiempo que mantiene una clara separación entre los distintos componentes de tu aplicación. Cada grupo de ajustes puede tener su propio prefijo de variable de entorno y sus propias reglas de validación.

El enfoque del archivo .env funciona con plataformas de despliegue como Heroku, AWS y Docker, donde las variables de entorno son la forma estándar de configurar las aplicaciones. Tu aplicación obtiene seguridad y validación de tipos mientras sigue los patrones de configuración nativos de la nube que esperan los equipos de operaciones.

Estos patrones constituyen la base para construir aplicaciones mantenibles que gestionen la complejidad del mundo real. El sistema de validación de Pydantic se adapta a tus requisitos específicos a la vez que proporciona mensajes de error claros y documentación automática que ayuda a todo tu equipo a comprender las estructuras de datos que espera tu aplicación.

Conclusión

Ya has visto cómo Pydantic puede ahorrarte el tedioso trabajo de escribir código de validación a mano. En lugar de saturar tus funciones con comprobaciones en isinstance() y un tratamiento de errores personalizado, puedes definir tu estructura de datos una vez y dejar que Pydantic se encargue del resto.

Asegúrate de leer la sección de Preguntas Frecuentes más abajo para preguntas más específicas sobre Pydantic. Para obtener documentación completa y patrones avanzados, aquí tienes algunos recursos que puedes utilizar:

Preguntas frecuentes

¿La validación de Pydantic ralentiza mi aplicación?

Pydantic V2 está construido con Rust bajo el capó, lo que lo hace muy rápido. Para la mayoría de las aplicaciones, la sobrecarga de validación es mínima en comparación con los errores que evita. Puedes utilizar model_construct() para omitir la validación de fuentes de datos de confianza cuando el rendimiento sea crítico.

¿Cuándo debo utilizar Pydantic en lugar de las clases de datos de Python?

Utiliza Pydantic cuando necesites validación, trabajes con fuentes de datos externas o construyas API. Utiliza clases de datos para estructuras de datos internas sencillas en las que confíes en la entrada. Pydantic destaca cuando los datos proceden de usuarios, API o archivos de configuración.

¿Cómo manejo los errores de validación de Pydantic en las aplicaciones de producción?

Captura las excepciones de ValidationError y extrae la lista .errors() para obtener información estructurada de los errores. Cada error contiene los campos loc, msg, y type que te ayudan a proporcionar mensajes de error fáciles de usar o a registrar información de depuración detallada.

¿Puedo añadir Pydantic al código existente sin romperlo todo?

Sí. Empieza envolviendo los puntos de entrada de datos externos (peticiones API, análisis de archivos) con modelos Pydantic. Puedes convertir gradualmente las estructuras de datos internas. Pydantic funciona junto al código existente y no requiere cambiar toda tu base de código de una vez.

¿Cuál es el error más común al empezar con Pydantic?

No entender el tipo de coacción. Pydantic convierte los tipos compatibles (como la cadena "123" al entero 123) por defecto. Si necesitas una comprobación de tipos estricta, utiliza Field(strict=True) o establece el modo estricto en la configuración de tu modelo para evitar las conversiones automáticas.


Bex Tuychiev's photo
Author
Bex Tuychiev
LinkedIn

Soy un creador de contenidos de ciencia de datos con más de 2 años de experiencia y uno de los mayores seguidores en Medium. Me gusta escribir artículos detallados sobre IA y ML con un estilo un poco sarcastıc, porque hay que hacer algo para que sean un poco menos aburridos. He publicado más de 130 artículos y un curso DataCamp, y estoy preparando otro. Mi contenido ha sido visto por más de 5 millones de ojos, 20.000 de los cuales se convirtieron en seguidores tanto en Medium como en LinkedIn. 

Temas

¡Aprende ciencia de datos con DataCamp!

Curso

Software Engineering Principles in Python

4 h
54.3K
Learn about modularity, documentation, and automated testing to help you solve data science problems more quickly and reliably.
Ver detallesRight Arrow
Comienza el curso
Ver másRight Arrow
Relacionado
Machine Learning Jobs Header

Tutorial

Cómo utilizar Pytest para pruebas unitarias

Explore qué es Pytest y para qué se utiliza mientras lo compara con otros métodos de prueba de software.
Kurtis Pykes 's photo

Kurtis Pykes

13 min

Tutorial

Tutorial de FastAPI: Introducción al uso de FastAPI

Explore el marco FastAPI y descubra cómo puede utilizarlo para crear API en Python.
Moez Ali's photo

Moez Ali

13 min

Tutorial

Tutorial de Pyspark: Primeros pasos con Pyspark

Descubre qué es Pyspark y cómo se puede utilizar, con ejemplos.
Natassha Selvaraj's photo

Natassha Selvaraj

10 min

Tutorial

Tutorial Python Docstrings : Ejemplos y Formato de Cadenas Doc Pydoc, Numpy, Sphinx

Aprende sobre las Docstrings de Python. Encuentra diferentes ejemplos y tipos de formato de docstrings para Sphinx, Numpy y Pydoc.
Aditya Sharma's photo

Aditya Sharma

15 min

SQLAlchemy_Tutorial.

Tutorial

Tutorial de SQLAlchemy con ejemplos

Aprende a acceder y ejecutar consultas SQL en todo tipo de bases de datos relacionales utilizando objetos Python.
Abid Ali Awan's photo

Abid Ali Awan

13 min

Tutorial

Conversión de tipos de datos en Python: Una guía con ejemplos

En este tutorial de Python, abordarás la conversión implícita y explícita de tipos de datos de estructuras de datos primitivas y no primitivas con la ayuda de ejemplos de código.
Sejal Jaiswal's photo

Sejal Jaiswal

14 min

Ver másVer más