Implementación completa de CRUD con ViewSets
Ahora que conoces los fundamentos de serializadores y ViewSets, es momento de construir una API REST completa que integre todas las operaciones CRUD con una base de datos real. Vamos a desarrollar un sistema de gestión de productos que demuestre cómo Django REST Framework maneja automáticamente las operaciones de creación, lectura, actualización y eliminació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
Definición del modelo
Comenzaremos definiendo un modelo que represente un producto en nuestro sistema. Este modelo incluirá campos comunes que encontrarías en cualquier aplicación comercial:
# models.py
from django.db import models
from django.core.validators import MinValueValidator
from decimal import Decimal
class Categoria(models.Model):
nombre = models.CharField(max_length=100, unique=True)
descripcion = models.TextField(blank=True)
activa = models.BooleanField(default=True)
def __str__(self):
return self.nombre
class Meta:
verbose_name_plural = "categorías"
class Producto(models.Model):
nombre = models.CharField(max_length=200)
descripcion = models.TextField()
precio = models.DecimalField(
max_digits=10,
decimal_places=2,
validators=[MinValueValidator(Decimal('0.01'))]
)
stock = models.PositiveIntegerField(default=0)
categoria = models.ForeignKey(
Categoria,
on_delete=models.CASCADE,
related_name='productos'
)
activo = models.BooleanField(default=True)
fecha_creacion = models.DateTimeField(auto_now_add=True)
fecha_actualizacion = models.DateTimeField(auto_now=True)
def __str__(self):
return self.nombre
class Meta:
ordering = ['-fecha_creacion']
Serializadores para CRUD completo
Los serializadores deben manejar tanto la serialización de salida como la validación de entrada para todas las operaciones CRUD:
# serializers.py
from rest_framework import serializers
from .models import Producto, Categoria
class CategoriaSerializer(serializers.ModelSerializer):
productos_count = serializers.SerializerMethodField()
class Meta:
model = Categoria
fields = ['id', 'nombre', 'descripcion', 'activa', 'productos_count']
def get_productos_count(self, obj):
return obj.productos.filter(activo=True).count()
class ProductoSerializer(serializers.ModelSerializer):
categoria_nombre = serializers.CharField(source='categoria.nombre', read_only=True)
class Meta:
model = Producto
fields = [
'id', 'nombre', 'descripcion', 'precio', 'stock',
'categoria', 'categoria_nombre', 'activo',
'fecha_creacion', 'fecha_actualizacion'
]
read_only_fields = ['fecha_creacion', 'fecha_actualizacion']
def validate_precio(self, value):
if value <= 0:
raise serializers.ValidationError("El precio debe ser mayor que cero")
return value
def validate_stock(self, value):
if value < 0:
raise serializers.ValidationError("El stock no puede ser negativo")
return value
ViewSets con operaciones CRUD completas
El ModelViewSet proporciona automáticamente todas las operaciones CRUD, pero podemos personalizarlo para añadir lógica de negocio específica:
# views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.db.models import Q
from .models import Producto, Categoria
from .serializers import ProductoSerializer, CategoriaSerializer
class CategoriaViewSet(viewsets.ModelViewSet):
queryset = Categoria.objects.all()
serializer_class = CategoriaSerializer
def destroy(self, request, *args, **kwargs):
categoria = self.get_object()
if categoria.productos.exists():
return Response(
{'error': 'No se puede eliminar una categoría con productos asociados'},
status=status.HTTP_400_BAD_REQUEST
)
return super().destroy(request, *args, **kwargs)
class ProductoViewSet(viewsets.ModelViewSet):
queryset = Producto.objects.select_related('categoria').all()
serializer_class = ProductoSerializer
def get_queryset(self):
queryset = super().get_queryset()
# Filtrado por categoría
categoria_id = self.request.query_params.get('categoria')
if categoria_id:
queryset = queryset.filter(categoria_id=categoria_id)
# Filtrado por estado activo
activo = self.request.query_params.get('activo')
if activo is not None:
queryset = queryset.filter(activo=activo.lower() == 'true')
# Búsqueda por nombre
busqueda = self.request.query_params.get('buscar')
if busqueda:
queryset = queryset.filter(
Q(nombre__icontains=busqueda) |
Q(descripcion__icontains=busqueda)
)
return queryset
def perform_create(self, serializer):
# Lógica adicional al crear un producto
serializer.save()
def perform_update(self, serializer):
# Lógica adicional al actualizar un producto
serializer.save()
@action(detail=True, methods=['post'])
def actualizar_stock(self, request, pk=None):
producto = self.get_object()
nuevo_stock = request.data.get('stock')
if nuevo_stock is None:
return Response(
{'error': 'El campo stock es requerido'},
status=status.HTTP_400_BAD_REQUEST
)
try:
nuevo_stock = int(nuevo_stock)
if nuevo_stock < 0:
raise ValueError()
except (ValueError, TypeError):
return Response(
{'error': 'El stock debe ser un número entero positivo'},
status=status.HTTP_400_BAD_REQUEST
)
producto.stock = nuevo_stock
producto.save()
serializer = self.get_serializer(producto)
return Response(serializer.data)
Configuración de URLs
Las URLs se configuran de manera simple utilizando el router de DRF, que automáticamente genera todos los endpoints CRUD:
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductoViewSet, CategoriaViewSet
router = DefaultRouter()
router.register(r'productos', ProductoViewSet)
router.register(r'categorias', CategoriaViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
Endpoints generados automáticamente
Con esta configuración, el router genera automáticamente los siguientes endpoints:
Para productos:
GET /api/productos/ # Listar todos los productos
POST /api/productos/ # Crear un nuevo producto
GET /api/productos/{id}/ # Obtener un producto específico
PUT /api/productos/{id}/ # Actualizar completamente un producto
PATCH /api/productos/{id}/ # Actualizar parcialmente un producto
DELETE /api/productos/{id}/ # Eliminar un producto
POST /api/productos/{id}/actualizar_stock/ # Acción personalizada
Para categorías:
GET /api/categorias/ # Listar todas las categorías
POST /api/categorias/ # Crear una nueva categoría
GET /api/categorias/{id}/ # Obtener una categoría específica
PUT /api/categorias/{id}/ # Actualizar completamente una categoría
PATCH /api/categorias/{id}/ # Actualizar parcialmente una categoría
DELETE /api/categorias/{id}/ # Eliminar una categoría
Ejemplos de uso de la API
Crear un producto:
POST /api/productos/
{
"nombre": "Laptop Gaming",
"descripcion": "Laptop para gaming de alta gama",
"precio": "1299.99",
"stock": 10,
"categoria": 1,
"activo": true
}
Actualizar parcialmente un producto:
PATCH /api/productos/1/
{
"precio": "1199.99",
"stock": 8
}
Filtrar productos por categoría:
GET /api/productos/?categoria=1&activo=true&buscar=laptop
Manejo de errores y validaciones
El ViewSet maneja automáticamente los errores de validación y devuelve respuestas HTTP apropiadas:
# Ejemplo de respuesta de error de validación
{
"precio": ["El precio debe ser mayor que cero"],
"stock": ["El stock no puede ser negativo"]
}
Para errores de objetos no encontrados, DRF devuelve automáticamente un error 404:
{
"detail": "No encontrado."
}
Esta implementación proporciona una API REST completa y funcional que maneja todas las operaciones CRUD de manera eficiente, incluyendo validaciones, filtrado y manejo de errores. El código es escalable y sigue las mejores prácticas de Django REST Framework.
Aprendizajes de esta lección
- Comprender la definición de modelos Django para representar entidades comerciales.
- Aprender a crear serializadores que validen y transformen datos para operaciones CRUD.
- Utilizar ViewSets para implementar operaciones CRUD completas y personalizadas.
- Configurar routers para generar automáticamente endpoints RESTful.
- Manejar validaciones, filtrados y respuestas de error en una API REST.
Completa Django 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