Estructura básica de DELETE
El método DELETE es el último de los métodos HTTP fundamentales que necesitas dominar para crear APIs REST completas. Su propósito es eliminar recursos específicos del servidor, completando así el conjunto de operaciones CRUD (Create, Read, Update, Delete).
Sintaxis del decorador DELETE
En FastAPI, utilizas el decorador @app.delete()
para definir endpoints que eliminen recursos. La estructura básica sigue el mismo patrón que has visto con otros métodos HTTP:
from fastapi import FastAPI
app = FastAPI()
@app.delete("/usuarios/{usuario_id}")
def eliminar_usuario(usuario_id: int):
# Lógica de eliminación
pass
Ejemplo práctico con datos en memoria
Vamos a crear un ejemplo completo utilizando una lista de usuarios almacenada en memoria. Primero establecemos los datos iniciales:
from fastapi import FastAPI, HTTPException
app = FastAPI()
# Base de datos simulada en memoria
usuarios = [
{"id": 1, "nombre": "Ana", "email": "ana@email.com"},
{"id": 2, "nombre": "Carlos", "email": "carlos@email.com"},
{"id": 3, "nombre": "María", "email": "maria@email.com"}
]
Ahora implementamos el endpoint de eliminación:
@app.delete("/usuarios/{usuario_id}")
def eliminar_usuario(usuario_id: int):
# Buscar el usuario por ID
for i, usuario in enumerate(usuarios):
if usuario["id"] == usuario_id:
# Eliminar el usuario de la lista
usuario_eliminado = usuarios.pop(i)
return {"mensaje": f"Usuario {usuario_eliminado['nombre']} eliminado correctamente"}
# Si no se encuentra el usuario
raise HTTPException(status_code=404, detail="Usuario no encontrado")
Manejo de errores en DELETE
El manejo de errores en operaciones DELETE es crucial. Los casos más comunes incluyen:
- Recurso no encontrado: Cuando intentas eliminar algo que no existe
- Recurso ya eliminado: Cuando el recurso fue eliminado previamente
@app.delete("/productos/{producto_id}")
def eliminar_producto(producto_id: int):
# Lista de productos simulada
productos = [
{"id": 1, "nombre": "Laptop", "precio": 999.99},
{"id": 2, "nombre": "Mouse", "precio": 25.50}
]
# Verificar si el producto existe
producto_encontrado = None
indice = -1
for i, producto in enumerate(productos):
if producto["id"] == producto_id:
producto_encontrado = producto
indice = i
break
if producto_encontrado is None:
raise HTTPException(
status_code=404,
detail=f"Producto con ID {producto_id} no encontrado"
)
# Eliminar el producto
productos.pop(indice)
return {"mensaje": "Producto eliminado correctamente", "producto": producto_encontrado}
Eliminación con validaciones adicionales
En aplicaciones reales, a menudo necesitas validaciones adicionales antes de eliminar un recurso:
@app.delete("/tareas/{tarea_id}")
def eliminar_tarea(tarea_id: int):
tareas = [
{"id": 1, "titulo": "Estudiar FastAPI", "completada": False},
{"id": 2, "titulo": "Hacer ejercicios", "completada": True},
{"id": 3, "titulo": "Revisar código", "completada": False}
]
# Buscar la tarea
for i, tarea in enumerate(tareas):
if tarea["id"] == tarea_id:
# Validación: solo eliminar tareas completadas
if not tarea["completada"]:
raise HTTPException(
status_code=400,
detail="No se pueden eliminar tareas incompletas"
)
# Eliminar la tarea
tarea_eliminada = tareas.pop(i)
return {
"mensaje": "Tarea eliminada correctamente",
"tarea": tarea_eliminada
}
raise HTTPException(status_code=404, detail="Tarea no encontrada")
Patrón de búsqueda y eliminación
Un patrón común en operaciones DELETE es separar la lógica de búsqueda de la eliminación para mayor claridad:
def buscar_usuario_por_id(usuario_id: int):
"""Función auxiliar para buscar un usuario"""
for i, usuario in enumerate(usuarios):
if usuario["id"] == usuario_id:
return i, usuario
return None, None
@app.delete("/usuarios/{usuario_id}")
def eliminar_usuario(usuario_id: int):
indice, usuario = buscar_usuario_por_id(usuario_id)
if usuario is None:
raise HTTPException(status_code=404, detail="Usuario no encontrado")
# Eliminar el usuario
usuarios.pop(indice)
return {
"mensaje": "Usuario eliminado correctamente",
"usuario_eliminado": usuario
}
Esta separación de responsabilidades hace que el código sea más legible y reutilizable, especialmente cuando necesitas la misma lógica de búsqueda en otros endpoints.
¿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
Respuestas apropiadas para eliminación
Cuando implementas operaciones DELETE, es fundamental devolver códigos de estado HTTP apropiados que comuniquen claramente el resultado de la operación. FastAPI te permite especificar estos códigos de manera explícita para crear APIs más profesionales y predecibles.
Código 204 - No Content
El código 204 No Content es la respuesta estándar para eliminaciones exitosas cuando no necesitas devolver información adicional. Indica que la operación se completó correctamente pero no hay contenido que mostrar:
from fastapi import FastAPI, HTTPException, status
from fastapi.responses import Response
app = FastAPI()
usuarios = [
{"id": 1, "nombre": "Ana", "email": "ana@email.com"},
{"id": 2, "nombre": "Carlos", "email": "carlos@email.com"}
]
@app.delete("/usuarios/{usuario_id}", status_code=status.HTTP_204_NO_CONTENT)
def eliminar_usuario(usuario_id: int):
for i, usuario in enumerate(usuarios):
if usuario["id"] == usuario_id:
usuarios.pop(i)
return Response(status_code=status.HTTP_204_NO_CONTENT)
raise HTTPException(status_code=404, detail="Usuario no encontrado")
Código 200 - OK con información
Cuando necesitas confirmar la eliminación devolviendo detalles del recurso eliminado, utiliza el código 200. Esta aproximación es útil para auditoría o confirmación visual:
@app.delete("/productos/{producto_id}")
def eliminar_producto(producto_id: int):
productos = [
{"id": 1, "nombre": "Laptop", "precio": 999.99, "stock": 5},
{"id": 2, "nombre": "Mouse", "precio": 25.50, "stock": 10}
]
for i, producto in enumerate(productos):
if producto["id"] == producto_id:
producto_eliminado = productos.pop(i)
return {
"mensaje": "Producto eliminado correctamente",
"producto": producto_eliminado,
"timestamp": "2024-01-15T10:30:00Z"
}
raise HTTPException(status_code=404, detail="Producto no encontrado")
Respuestas de error específicas
Las respuestas de error en DELETE deben ser descriptivas y ayudar al cliente a entender qué ocurrió:
@app.delete("/pedidos/{pedido_id}")
def eliminar_pedido(pedido_id: int):
pedidos = [
{"id": 1, "cliente": "Ana", "estado": "pendiente", "total": 150.00},
{"id": 2, "cliente": "Carlos", "estado": "enviado", "total": 75.50}
]
for i, pedido in enumerate(pedidos):
if pedido["id"] == pedido_id:
# Validar si se puede eliminar
if pedido["estado"] == "enviado":
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail={
"error": "Conflicto de estado",
"mensaje": "No se pueden eliminar pedidos ya enviados",
"pedido_id": pedido_id,
"estado_actual": pedido["estado"]
}
)
pedidos.pop(i)
return Response(status_code=status.HTTP_204_NO_CONTENT)
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"error": "Recurso no encontrado",
"mensaje": f"El pedido con ID {pedido_id} no existe",
"pedido_id": pedido_id
}
)
Respuestas consistentes con modelos Pydantic
Para mantener consistencia en las respuestas, puedes definir modelos Pydantic específicos para las respuestas de eliminación:
from pydantic import BaseModel
from typing import Optional
class RespuestaEliminacion(BaseModel):
mensaje: str
recurso_eliminado: Optional[dict] = None
timestamp: str
class ErrorEliminacion(BaseModel):
error: str
mensaje: str
recurso_id: int
@app.delete("/comentarios/{comentario_id}", response_model=RespuestaEliminacion)
def eliminar_comentario(comentario_id: int):
comentarios = [
{"id": 1, "texto": "Excelente artículo", "autor": "Ana"},
{"id": 2, "texto": "Muy útil", "autor": "Carlos"}
]
for i, comentario in enumerate(comentarios):
if comentario["id"] == comentario_id:
comentario_eliminado = comentarios.pop(i)
return RespuestaEliminacion(
mensaje="Comentario eliminado correctamente",
recurso_eliminado=comentario_eliminado,
timestamp="2024-01-15T10:30:00Z"
)
raise HTTPException(
status_code=404,
detail=ErrorEliminacion(
error="Recurso no encontrado",
mensaje="El comentario especificado no existe",
recurso_id=comentario_id
).dict()
)
Eliminación idempotente
Las operaciones DELETE deben ser idempotentes, es decir, ejecutar la misma eliminación múltiples veces debe producir el mismo resultado. Esto significa que intentar eliminar un recurso ya eliminado no debe generar un error:
@app.delete("/archivos/{archivo_id}")
def eliminar_archivo(archivo_id: int):
archivos = [
{"id": 1, "nombre": "documento.pdf", "eliminado": False},
{"id": 2, "nombre": "imagen.jpg", "eliminado": True} # Ya eliminado
]
for archivo in archivos:
if archivo["id"] == archivo_id:
if archivo["eliminado"]:
# Ya estaba eliminado - respuesta idempotente
return Response(status_code=status.HTTP_204_NO_CONTENT)
else:
# Marcar como eliminado
archivo["eliminado"] = True
return Response(status_code=status.HTTP_204_NO_CONTENT)
# Archivo nunca existió
raise HTTPException(status_code=404, detail="Archivo no encontrado")
Respuestas con información de contexto
En algunos casos, es útil proporcionar información adicional sobre el impacto de la eliminación:
@app.delete("/categorias/{categoria_id}")
def eliminar_categoria(categoria_id: int):
categorias = [
{"id": 1, "nombre": "Electrónicos", "productos_count": 15},
{"id": 2, "nombre": "Ropa", "productos_count": 0}
]
for i, categoria in enumerate(categorias):
if categoria["id"] == categoria_id:
if categoria["productos_count"] > 0:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail={
"error": "Eliminación no permitida",
"mensaje": "No se puede eliminar una categoría con productos asociados",
"productos_asociados": categoria["productos_count"]
}
)
categoria_eliminada = categorias.pop(i)
return {
"mensaje": "Categoría eliminada correctamente",
"categoria": categoria_eliminada,
"impacto": "Sin productos afectados"
}
raise HTTPException(status_code=404, detail="Categoría no encontrada")
La elección del tipo de respuesta depende de las necesidades específicas de tu API y las expectativas de los clientes que la consumen. Lo importante es mantener consistencia en toda tu aplicación y documentar claramente el comportamiento esperado.
Aprendizajes de esta lección
- Comprender la función y sintaxis del método DELETE en FastAPI.
- Implementar endpoints para eliminar recursos con manejo adecuado de errores.
- Aplicar validaciones adicionales antes de eliminar recursos.
- Conocer y utilizar códigos de estado HTTP apropiados para respuestas de eliminación.
- Diseñar respuestas consistentes y idempotentes para operaciones DELETE.
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