PUT para actualización completa
El método PUT está diseñado para reemplazar completamente un recurso existente. Cuando envías una petición PUT, estás indicando que quieres sustituir todos los datos del recurso con la información que proporcionas en el cuerpo de la petición.
En FastAPI, implementar endpoints PUT es similar a POST, pero con una diferencia conceptual importante: PUT actualiza recursos existentes en lugar de crear nuevos. Esto significa que normalmente necesitarás un identificador para localizar el recurso que quieres actualizar.
Implementación básica de PUT
Vamos a crear un ejemplo práctico con una lista de usuarios en memoria:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List
app = FastAPI()
class Usuario(BaseModel):
nombre: str
email: str
edad: int
# Base de datos simulada en memoria
usuarios_db = [
{"id": 1, "nombre": "Ana García", "email": "ana@email.com", "edad": 28},
{"id": 2, "nombre": "Carlos López", "email": "carlos@email.com", "edad": 35}
]
@app.put("/usuarios/{usuario_id}")
def actualizar_usuario_completo(usuario_id: int, usuario: Usuario):
# Buscar el usuario por ID
for i, user in enumerate(usuarios_db):
if user["id"] == usuario_id:
# Reemplazar completamente el usuario
usuarios_db[i] = {
"id": usuario_id,
"nombre": usuario.nombre,
"email": usuario.email,
"edad": usuario.edad
}
return usuarios_db[i]
# Si no existe, devolver error 404
raise HTTPException(status_code=404, detail="Usuario no encontrado")
En este ejemplo, el endpoint PUT recibe el ID del usuario en la URL y los nuevos datos completos en el cuerpo de la petición. El usuario existente se reemplaza completamente con la nueva información.
Características del método PUT
El comportamiento de PUT tiene varias características importantes que debes entender:
Reemplazo completo: Cuando usas PUT, todos los campos del recurso se actualizan, incluso si no han cambiado. Si omites un campo en la petición PUT, ese campo debería eliminarse o establecerse a un valor por defecto.
# Si envías esta petición PUT:
{
"nombre": "Ana Martínez",
"email": "ana.martinez@email.com"
# Falta el campo "edad"
}
# El comportamiento correcto sería establecer edad a None o un valor por defecto
Identificación del recurso: PUT siempre requiere identificar claramente qué recurso estás actualizando, normalmente a través de un ID en la URL.
Ejemplo con validación mejorada
Aquí tienes un ejemplo más robusto que maneja mejor los casos edge:
@app.put("/usuarios/{usuario_id}")
def actualizar_usuario_completo(usuario_id: int, usuario: Usuario):
if usuario_id <= 0:
raise HTTPException(status_code=400, detail="ID de usuario inválido")
# Buscar el índice del usuario
usuario_index = None
for i, user in enumerate(usuarios_db):
if user["id"] == usuario_id:
usuario_index = i
break
if usuario_index is None:
raise HTTPException(status_code=404, detail="Usuario no encontrado")
# Crear el usuario actualizado
usuario_actualizado = {
"id": usuario_id,
"nombre": usuario.nombre,
"email": usuario.email,
"edad": usuario.edad
}
# Reemplazar en la base de datos
usuarios_db[usuario_index] = usuario_actualizado
return {
"mensaje": "Usuario actualizado completamente",
"usuario": usuario_actualizado
}
Endpoint para obtener usuarios
Para probar nuestro endpoint PUT, necesitamos también un endpoint GET que nos permita ver los usuarios:
@app.get("/usuarios")
def obtener_usuarios():
return usuarios_db
@app.get("/usuarios/{usuario_id}")
def obtener_usuario(usuario_id: int):
for user in usuarios_db:
if user["id"] == usuario_id:
return user
raise HTTPException(status_code=404, detail="Usuario no encontrado")
Probando el endpoint PUT
Puedes probar el endpoint PUT enviando una petición como esta:
PUT /usuarios/1
Content-Type: application/json
{
"nombre": "Ana Martínez",
"email": "ana.martinez@email.com",
"edad": 29
}
La respuesta será:
{
"mensaje": "Usuario actualizado completamente",
"usuario": {
"id": 1,
"nombre": "Ana Martínez",
"email": "ana.martinez@email.com",
"edad": 29
}
}
Cuándo usar PUT
El método PUT es ideal cuando necesitas:
- Actualizar todos los campos de un recurso de una vez
- Reemplazar completamente la información existente
- Garantizar consistencia en los datos del recurso
- Simplificar la lógica de actualización cuando siempre trabajas con objetos completos
Es importante recordar que PUT está pensado para actualizaciones completas. Si solo necesitas cambiar algunos campos específicos, el método PATCH será más apropiado, como veremos en la siguiente sección.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
PATCH para actualización parcial
El método PATCH está diseñado para realizar actualizaciones parciales de un recurso existente. A diferencia de PUT, que reemplaza completamente el recurso, PATCH te permite modificar únicamente los campos específicos que necesitas cambiar, manteniendo el resto de la información intacta.
Esta característica hace que PATCH sea especialmente útil cuando trabajas con recursos que tienen muchos campos y solo necesitas actualizar algunos de ellos. Es más eficiente y reduce el riesgo de sobrescribir accidentalmente datos que no querías modificar.
Implementación básica de PATCH
Para implementar PATCH en FastAPI, necesitamos crear un modelo que permita campos opcionales. Utilizaremos la misma base de datos de usuarios del ejemplo anterior:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
class UsuarioPatch(BaseModel):
nombre: Optional[str] = None
email: Optional[str] = None
edad: Optional[int] = None
# Base de datos simulada (la misma del ejemplo anterior)
usuarios_db = [
{"id": 1, "nombre": "Ana García", "email": "ana@email.com", "edad": 28},
{"id": 2, "nombre": "Carlos López", "email": "carlos@email.com", "edad": 35}
]
@app.patch("/usuarios/{usuario_id}")
def actualizar_usuario_parcial(usuario_id: int, usuario_patch: UsuarioPatch):
# Buscar el usuario por ID
for i, user in enumerate(usuarios_db):
if user["id"] == usuario_id:
# Actualizar solo los campos proporcionados
if usuario_patch.nombre is not None:
usuarios_db[i]["nombre"] = usuario_patch.nombre
if usuario_patch.email is not None:
usuarios_db[i]["email"] = usuario_patch.email
if usuario_patch.edad is not None:
usuarios_db[i]["edad"] = usuario_patch.edad
return usuarios_db[i]
raise HTTPException(status_code=404, detail="Usuario no encontrado")
El modelo UsuarioPatch define todos los campos como opcionales usando Optional[tipo] = None
. Esto permite que el cliente envíe solo los campos que quiere actualizar.
Mejorando la implementación con exclude_unset
Pydantic ofrece una funcionalidad muy útil llamada exclude_unset
que nos permite trabajar de forma más elegante con actualizaciones parciales:
@app.patch("/usuarios/{usuario_id}")
def actualizar_usuario_parcial(usuario_id: int, usuario_patch: UsuarioPatch):
# Buscar el usuario
usuario_index = None
for i, user in enumerate(usuarios_db):
if user["id"] == usuario_id:
usuario_index = i
break
if usuario_index is None:
raise HTTPException(status_code=404, detail="Usuario no encontrado")
# Obtener solo los campos que fueron enviados
datos_actualizacion = usuario_patch.model_dump(exclude_unset=True)
# Actualizar solo los campos proporcionados
for campo, valor in datos_actualizacion.items():
usuarios_db[usuario_index][campo] = valor
return {
"mensaje": "Usuario actualizado parcialmente",
"usuario": usuarios_db[usuario_index]
}
El método model_dump(exclude_unset=True)
devuelve un diccionario que contiene únicamente los campos que fueron explícitamente establecidos en la petición, ignorando aquellos que mantienen su valor por defecto.
Validación específica para PATCH
Cuando implementas PATCH, es importante validar que al menos un campo sea proporcionado para la actualización:
@app.patch("/usuarios/{usuario_id}")
def actualizar_usuario_parcial(usuario_id: int, usuario_patch: UsuarioPatch):
if usuario_id <= 0:
raise HTTPException(status_code=400, detail="ID de usuario inválido")
# Verificar que se proporcione al menos un campo para actualizar
datos_actualizacion = usuario_patch.model_dump(exclude_unset=True)
if not datos_actualizacion:
raise HTTPException(
status_code=400,
detail="Debe proporcionar al menos un campo para actualizar"
)
# Buscar el usuario
usuario_index = None
for i, user in enumerate(usuarios_db):
if user["id"] == usuario_id:
usuario_index = i
break
if usuario_index is None:
raise HTTPException(status_code=404, detail="Usuario no encontrado")
# Aplicar las actualizaciones
for campo, valor in datos_actualizacion.items():
usuarios_db[usuario_index][campo] = valor
return {
"mensaje": f"Campos actualizados: {', '.join(datos_actualizacion.keys())}",
"usuario": usuarios_db[usuario_index]
}
Ejemplos de uso de PATCH
Aquí tienes varios ejemplos de cómo usar el endpoint PATCH:
Actualizar solo el nombre:
PATCH /usuarios/1
Content-Type: application/json
{
"nombre": "Ana Martínez"
}
Actualizar email y edad:
PATCH /usuarios/1
Content-Type: application/json
{
"email": "ana.nueva@email.com",
"edad": 30
}
Actualizar solo la edad:
PATCH /usuarios/2
Content-Type: application/json
{
"edad": 36
}
Validaciones adicionales con PATCH
Puedes agregar validaciones específicas para ciertos campos durante las actualizaciones parciales:
from pydantic import validator
class UsuarioPatch(BaseModel):
nombre: Optional[str] = None
email: Optional[str] = None
edad: Optional[int] = None
@validator('edad')
def validar_edad(cls, v):
if v is not None and (v < 0 or v > 120):
raise ValueError('La edad debe estar entre 0 y 120 años')
return v
@validator('email')
def validar_email(cls, v):
if v is not None and '@' not in v:
raise ValueError('El email debe contener el símbolo @')
return v
Cuándo usar PATCH
El método PATCH es la opción ideal cuando:
- Solo necesitas modificar algunos campos específicos de un recurso
- Quieres preservar la información existente que no estás actualizando
- Trabajas con recursos grandes donde enviar todos los datos sería ineficiente
- Implementas funcionalidades como "cambiar solo la contraseña" o "actualizar solo el estado"
La principal ventaja de PATCH sobre PUT es su flexibilidad y eficiencia. Los clientes pueden enviar exactamente los datos que necesitan cambiar, sin preocuparse por el resto de la información del recurso.
Aprendizajes de esta lección
- Comprender la diferencia conceptual entre los métodos PUT y PATCH.
- Implementar un endpoint PUT para actualización completa de recursos en FastAPI.
- Implementar un endpoint PATCH para actualización parcial de recursos con campos opcionales.
- Aplicar validaciones específicas para cada método y manejar errores comunes.
- Saber cuándo utilizar PUT o PATCH según el caso de uso y eficiencia deseada.
Completa FastAPI y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs