Django Avanzado

Formularios, Django Admin, middleware, sesiones, autenticación JWT y desarrollo de proyectos complejos

Objetivos de Aprendizaje

Gestión de Formularios

Sistema de Formularios en Django

Django proporciona un sistema robusto para manejar formularios HTML, validación de datos y renderizado automático con protección CSRF integrada.

Formularios Básicos

Formularios simples con validación automática

ModelForms

Formularios basados en modelos de datos

Formularios Personalizados

Validación y widgets personalizados

Formsets

Múltiples formularios en una sola vista

Formularios Básicos

# forms.py
from django import forms
from django.core.exceptions import ValidationError

class ContactoForm(forms.Form):
    nombre = forms.CharField(
        max_length=100,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Tu nombre completo'
        })
    )
    email = forms.EmailField(
        widget=forms.EmailInput(attrs={
            'class': 'form-control',
            'placeholder': 'tu@email.com'
        })
    )
    asunto = forms.CharField(
        max_length=200,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    mensaje = forms.CharField(
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 5,
            'placeholder': 'Escribe tu mensaje aquí...'
        })
    )
    acepta_terminos = forms.BooleanField(
        required=True,
        error_messages={'required': 'Debes aceptar los términos'}
    )
    
    def clean_email(self):
        email = self.cleaned_data['email']
        if not email.endswith('@empresa.com'):
            raise ValidationError('Solo emails corporativos permitidos')
        return email
    
    def clean(self):
        cleaned_data = super().clean()
        nombre = cleaned_data.get('nombre')
        mensaje = cleaned_data.get('mensaje')
        
        if nombre and mensaje and nombre.lower() in mensaje.lower():
            raise ValidationError('El mensaje no puede contener tu nombre')
        
        return cleaned_data

ModelForms

# forms.py
from django import forms
from .models import Articulo, Categoria

class ArticuloForm(forms.ModelForm):
    class Meta:
        model = Articulo
        fields = ['titulo', 'contenido', 'categoria', 'etiquetas', 'imagen']
        widgets = {
            'titulo': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': 'Título del artículo'
            }),
            'contenido': forms.Textarea(attrs={
                'class': 'form-control editor',
                'rows': 10
            }),
            'categoria': forms.Select(attrs={'class': 'form-control'}),
            'etiquetas': forms.CheckboxSelectMultiple(),
            'imagen': forms.FileInput(attrs={
                'class': 'form-control',
                'accept': 'image/*'
            })
        }
        labels = {
            'titulo': 'Título del Artículo',
            'contenido': 'Contenido',
            'categoria': 'Categoría',
            'etiquetas': 'Etiquetas',
            'imagen': 'Imagen Destacada'
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['categoria'].queryset = Categoria.objects.filter(activa=True)
        self.fields['titulo'].help_text = 'Máximo 200 caracteres'
    
    def save(self, commit=True):
        articulo = super().save(commit=False)
        articulo.slug = self.generate_slug(articulo.titulo)
        if commit:
            articulo.save()
            self.save_m2m()
        return articulo
    
    def generate_slug(self, titulo):
        from django.utils.text import slugify
        return slugify(titulo)

Django Admin

Panel de Administración

Django Admin proporciona una interfaz web automática para gestionar los datos de tu aplicación, completamente personalizable y extensible.

Generación Automática

Interfaz automática basada en modelos

Personalización

Campos, filtros y acciones personalizadas

Gestión de Usuarios

Control de permisos y grupos

Reportes

Estadísticas y análisis de datos

Configuración Básica del Admin

# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Articulo, Categoria, Etiqueta

@admin.register(Categoria)
class CategoriaAdmin(admin.ModelAdmin):
    list_display = ['nombre', 'descripcion', 'activa', 'fecha_creacion']
    list_filter = ['activa', 'fecha_creacion']
    search_fields = ['nombre', 'descripcion']
    list_editable = ['activa']
    ordering = ['nombre']
    
    fieldsets = (
        ('Información Básica', {
            'fields': ('nombre', 'descripcion')
        }),
        ('Estado', {
            'fields': ('activa',),
            'classes': ('collapse',)
        }),
    )

@admin.register(Articulo)
class ArticuloAdmin(admin.ModelAdmin):
    list_display = ['titulo', 'autor', 'categoria', 'estado', 'vistas', 'fecha_creacion']
    list_filter = ['estado', 'categoria', 'fecha_creacion', 'autor']
    search_fields = ['titulo', 'contenido', 'autor__username']
    list_editable = ['estado']
    readonly_fields = ['slug', 'fecha_creacion', 'fecha_actualizacion', 'vistas']
    filter_horizontal = ['etiquetas']
    date_hierarchy = 'fecha_creacion'
    
    fieldsets = (
        ('Contenido', {
            'fields': ('titulo', 'slug', 'contenido', 'resumen')
        }),
        ('Clasificación', {
            'fields': ('categoria', 'etiquetas', 'imagen')
        }),
        ('Metadatos', {
            'fields': ('autor', 'estado', 'vistas'),
            'classes': ('collapse',)
        }),
        ('Fechas', {
            'fields': ('fecha_creacion', 'fecha_actualizacion'),
            'classes': ('collapse',)
        }),
    )
    
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(autor=request.user)
    
    def save_model(self, request, obj, form, change):
        if not change:  # Si es un nuevo objeto
            obj.autor = request.user
        super().save_model(request, obj, form, change)

Acciones Personalizadas

# admin.py (continuación)
from django.contrib import messages
from django.http import HttpResponse
import csv

class ArticuloAdmin(admin.ModelAdmin):
    # ... configuración anterior ...
    
    actions = ['publicar_articulos', 'archivar_articulos', 'exportar_csv']
    
    def publicar_articulos(self, request, queryset):
        updated = queryset.update(estado='publicado')
        self.message_user(
            request,
            f'{updated} artículos publicados exitosamente.',
            messages.SUCCESS
        )
    publicar_articulos.short_description = "Publicar artículos seleccionados"
    
    def archivar_articulos(self, request, queryset):
        updated = queryset.update(estado='archivado')
        self.message_user(
            request,
            f'{updated} artículos archivados.',
            messages.WARNING
        )
    archivar_articulos.short_description = "Archivar artículos seleccionados"
    
    def exportar_csv(self, request, queryset):
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="articulos.csv"'
        
        writer = csv.writer(response)
        writer.writerow(['Título', 'Autor', 'Estado', 'Fecha Creación'])
        
        for articulo in queryset:
            writer.writerow([
                articulo.titulo,
                articulo.autor.username,
                articulo.estado,
                articulo.fecha_creacion.strftime('%Y-%m-%d')
            ])
        
        return response
    exportar_csv.short_description = "Exportar a CSV"

Middleware y Sesiones

Middleware en Django

El middleware es un framework de hooks que procesa las peticiones y respuestas de Django globalmente, permitiendo modificar el comportamiento de la aplicación.

Flujo de Procesamiento

1
Request

Petición HTTP entrante

2
Middleware

Procesamiento de middleware

3
View

Ejecución de la vista

4
Response

Respuesta HTTP

Middleware Personalizado

# middleware.py
import time
from django.utils.deprecation import MiddlewareMixin
from django.http import HttpResponseForbidden
from django.core.cache import cache

class TimingMiddleware(MiddlewareMixin):
    """Middleware para medir tiempo de respuesta"""
    
    def process_request(self, request):
        request.start_time = time.time()
    
    def process_response(self, request, response):
        if hasattr(request, 'start_time'):
            duration = time.time() - request.start_time
            response['X-Response-Time'] = f'{duration:.3f}s'
        return response

class RateLimitMiddleware(MiddlewareMixin):
    """Middleware para limitar peticiones por IP"""
    
    def process_request(self, request):
        ip = self.get_client_ip(request)
        cache_key = f'rate_limit_{ip}'
        
        # Obtener número de peticiones en la última hora
        requests = cache.get(cache_key, 0)
        
        if requests >= 100:  # Límite de 100 peticiones por hora
            return HttpResponseForbidden('Rate limit exceeded')
        
        # Incrementar contador
        cache.set(cache_key, requests + 1, 3600)  # 1 hora
        
        return None
    
    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

class SecurityHeadersMiddleware(MiddlewareMixin):
    """Middleware para agregar headers de seguridad"""
    
    def process_response(self, request, response):
        response['X-Content-Type-Options'] = 'nosniff'
        response['X-Frame-Options'] = 'DENY'
        response['X-XSS-Protection'] = '1; mode=block'
        response['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
        return response

Gestión de Sesiones

# views.py - Gestión de sesiones
from django.shortcuts import render, redirect
from django.contrib import messages

def agregar_al_carrito(request, producto_id):
    """Agregar producto al carrito usando sesiones"""
    carrito = request.session.get('carrito', {})
    
    if str(producto_id) in carrito:
        carrito[str(producto_id)] += 1
    else:
        carrito[str(producto_id)] = 1
    
    request.session['carrito'] = carrito
    request.session.modified = True
    
    messages.success(request, 'Producto agregado al carrito')
    return redirect('tienda:productos')

def ver_carrito(request):
    """Ver contenido del carrito"""
    carrito = request.session.get('carrito', {})
    productos = []
    total = 0
    
    for producto_id, cantidad in carrito.items():
        try:
            producto = Producto.objects.get(id=producto_id)
            subtotal = producto.precio * cantidad
            productos.append({
                'producto': producto,
                'cantidad': cantidad,
                'subtotal': subtotal
            })
            total += subtotal
        except Producto.DoesNotExist:
            # Remover producto inexistente del carrito
            del carrito[producto_id]
            request.session['carrito'] = carrito
    
    context = {
        'productos': productos,
        'total': total,
        'cantidad_items': sum(carrito.values())
    }
    return render(request, 'tienda/carrito.html', context)

def limpiar_carrito(request):
    """Limpiar carrito de compras"""
    if 'carrito' in request.session:
        del request.session['carrito']
    messages.info(request, 'Carrito limpiado')
    return redirect('tienda:productos')

Autenticación JWT

JSON Web Tokens en Django

JWT proporciona un método seguro para transmitir información entre partes como un objeto JSON, ideal para APIs y aplicaciones SPA.

Seguridad

Tokens firmados digitalmente

Escalabilidad

Sin estado en el servidor

Multiplataforma

Compatible con cualquier cliente

Expiración

Tokens con tiempo de vida

Configuración JWT

# settings.py
from datetime import timedelta

INSTALLED_APPS = [
    # ... otras apps ...
    'rest_framework',
    'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUTH_HEADER_TYPES': ('Bearer',),
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
}

# urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView,
)

urlpatterns = [
    # ... otras URLs ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

Vistas con Autenticación JWT

# views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from django.contrib.auth import authenticate
from rest_framework_simplejwt.tokens import RefreshToken

@api_view(['POST'])
def registro_usuario(request):
    """Registro de nuevo usuario"""
    serializer = UsuarioSerializer(data=request.data)
    if serializer.is_valid():
        usuario = serializer.save()
        refresh = RefreshToken.for_user(usuario)
        return Response({
            'refresh': str(refresh),
            'access': str(refresh.access_token),
            'usuario': UsuarioSerializer(usuario).data
        }, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def login_personalizado(request):
    """Login personalizado con información adicional"""
    username = request.data.get('username')
    password = request.data.get('password')
    
    usuario = authenticate(username=username, password=password)
    if usuario:
        refresh = RefreshToken.for_user(usuario)
        return Response({
            'refresh': str(refresh),
            'access': str(refresh.access_token),
            'usuario': {
                'id': usuario.id,
                'username': usuario.username,
                'email': usuario.email,
                'first_name': usuario.first_name,
                'last_name': usuario.last_name,
            }
        })
    return Response(
        {'error': 'Credenciales inválidas'}, 
        status=status.HTTP_401_UNAUTHORIZED
    )

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def perfil_usuario(request):
    """Obtener perfil del usuario autenticado"""
    serializer = UsuarioSerializer(request.user)
    return Response(serializer.data)

@api_view(['PUT'])
@permission_classes([IsAuthenticated])
def actualizar_perfil(request):
    """Actualizar perfil del usuario"""
    serializer = UsuarioSerializer(request.user, data=request.data, partial=True)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Proyecto en GitHub

Proyecto Django Avanzado

Sistema completo de gestión de contenidos con todas las funcionalidades avanzadas implementadas en esta semana.

Formularios Avanzados

Sistema completo de formularios con validación personalizada

Admin Personalizado

Panel de administración con acciones y filtros personalizados

Middleware Custom

Middleware para seguridad, timing y rate limiting

Autenticación JWT

API REST con autenticación JWT completa

Resumen Semana 12

Formularios Avanzados

Dominio completo del sistema de formularios Django con validación personalizada, widgets y ModelForms.

Django Admin

Personalización avanzada del panel de administración con acciones personalizadas y gestión de permisos.

Middleware

Implementación de middleware personalizado para seguridad, timing y control de peticiones.

Gestión de Sesiones

Manejo avanzado de sesiones para carritos de compra y estado de usuario persistente.

Autenticación JWT

Implementación completa de autenticación JWT para APIs REST seguras y escalables.

Proyecto GitHub

Desarrollo de proyecto complejo integrando todas las funcionalidades avanzadas de Django.