# Django 6.0 en 2026: Claves Primarias Compuestas, Tareas en Segundo Plano y Preguntas de Entrevista Técnica > Guía completa de Django 6.0: claves primarias compuestas, tareas nativas en segundo plano, template partials, middleware CSP y preguntas de entrevista. - Published: 2026-06-09 - Updated: 2026-06-09 - Author: SharpSkill - Tags: django, python, composite-primary-keys, background-tasks, csp - Reading time: 10 min --- Django 6.0 marca un punto de inflexión para el desarrollo backend con Python en 2026. El framework que durante más de una década ha definido las mejores prácticas del ecosistema web de Python llega con un conjunto de funcionalidades que los equipos de desarrollo venían reclamando desde hace años. Las claves primarias compuestas eliminan una de las limitaciones históricas del ORM. El sistema nativo de tareas en segundo plano reduce la dependencia de infraestructura externa para operaciones asíncronas sencillas. Los template partials transforman la forma en que se organizan los componentes reutilizables en las plantillas. Y el middleware CSP integrado cierra una brecha de seguridad que hasta ahora solo cubrían paquetes de terceros. Esta guía recorre cada una de estas novedades con ejemplos de código listos para producción y cierra con las preguntas de entrevista que están apareciendo en los procesos de selección técnica de 2026. > **Línea de tiempo y requisitos de versión** > > Django sigue un ciclo predecible de lanzamientos: las versiones LTS reciben soporte extendido durante tres años, mientras que las versiones intermedias reciben soporte por dieciocho meses. Django 6.0 requiere Python 3.12 o superior. Las claves primarias compuestas, que se introdujeron como funcionalidad estable en Django 5.2, alcanzan su integración completa con todo el ecosistema del ORM en esta versión. ## Claves primarias compuestas con CompositePrimaryKey En el modelo relacional clásico, una clave primaria compuesta permite identificar de forma única un registro mediante la combinación de dos o más columnas. Es un patrón fundamental en bases de datos bien normalizadas, especialmente en tablas de relación muchos-a-muchos o tablas intermedias donde la unicidad del registro depende de la combinación de referencias a otras entidades. Hasta la llegada de Django 5.2, la única forma de representar este patrón en el ORM era mediante `unique_together` combinado con un campo `id` autoincrementable artificial, lo que rompía la correspondencia con el esquema relacional real de la base de datos. `CompositePrimaryKey` resuelve esta limitación de raíz. El campo se declara en el modelo indicando los nombres de las columnas de la base de datos que conforman la clave primaria. Django genera la restricción `PRIMARY KEY` correspondiente a nivel de base de datos y elimina la necesidad de un campo `id` autoincrementable. El siguiente ejemplo modela un sistema de inventario donde cada combinación de producto y almacén tiene una cantidad asociada: ```python # models.py from django.db import models class Product(models.Model): name = models.CharField(max_length=100) sku = models.CharField(max_length=20, unique=True) class Warehouse(models.Model): code = models.CharField(max_length=10, primary_key=True) location = models.CharField(max_length=200) class Inventory(models.Model): pk = models.CompositePrimaryKey("product_id", "warehouse_id") product = models.ForeignKey(Product, on_delete=models.CASCADE) warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE) quantity = models.PositiveIntegerField(default=0) last_restocked = models.DateTimeField(auto_now=True) ``` Es importante notar que `CompositePrimaryKey` recibe los nombres de las columnas subyacentes en la base de datos (`product_id`, `warehouse_id`), no los nombres de los campos del modelo Django (`product`, `warehouse`). Esta distinción es clave para evitar errores al definir las migraciones. ## Consultas con claves compuestas en el ORM La interacción con registros que utilizan `CompositePrimaryKey` mantiene la misma interfaz que cualquier modelo Django. La diferencia es que la propiedad `pk` devuelve una tupla de Python en lugar de un entero, y los métodos de filtrado aceptan tuplas como valor de búsqueda: ```python # views.py from .models import Inventory, Product, Warehouse # Create records laptop = Product.objects.create(name="Laptop Pro", sku="LP-001") warehouse_a = Warehouse.objects.create(code="WH-A", location="Berlin") item = Inventory.objects.create( product=laptop, warehouse=warehouse_a, quantity=50 ) # Access the composite pk as a tuple print(item.pk) # (1, "WH-A") # Filter by composite pk result = Inventory.objects.filter(pk=(1, "WH-A")).first() # Assign a composite pk directly new_item = Inventory(pk=(2, "WH-B")) print(new_item.product_id) # 2 print(new_item.warehouse_id) # "WH-B" ``` El ORM desempaqueta la tupla automáticamente en los campos correspondientes al momento de la asignación. Esto resulta particularmente valioso para equipos que trabajan con esquemas de base de datos heredados donde las tablas ya tienen claves compuestas definidas, o para proyectos que migran desde otros ORMs que ya soportaban este patrón. Django Admin, los serializadores de Django REST Framework y el sistema de migraciones reconocen los modelos con `CompositePrimaryKey` sin configuración adicional. ## Tareas nativas en segundo plano con el decorador @task Hasta ahora, ejecutar cualquier operación en segundo plano desde Django requería configurar un broker de mensajes, un sistema de workers y un paquete como Celery. Para muchos proyectos, especialmente los que solo necesitan enviar correos o generar un archivo PDF después de una solicitud HTTP, esta infraestructura resultaba desproporcionada respecto a la complejidad real de la tarea. Django 6.0 introduce el módulo `django.tasks` con un decorador `@task` y un método `.enqueue()` que permiten diferir la ejecución de funciones sin dependencias externas complejas. La convención es mantener un archivo `tasks.py` dentro de cada aplicación Django, siguiendo el mismo patrón que el framework utiliza para `models.py`, `views.py` y `admin.py`: ```python # tasks.py from django.tasks import task from django.core.mail import send_mail @task def send_welcome_email(user_email, username): """Send a welcome email after user registration.""" send_mail( subject="Welcome to the platform", message=f"Hello {username}, your account is ready.", from_email=None, # uses DEFAULT_FROM_EMAIL recipient_list=[user_email], ) return f"Email sent to {user_email}" @task def generate_report(report_id, format="pdf"): """Generate an export report asynchronously.""" from .services import ReportService report = ReportService.generate(report_id, format=format) return report.file_path ``` El despacho desde una vista se realiza mediante `.enqueue()`, que serializa los argumentos y los envía al backend configurado. La vista retorna de inmediato sin esperar a que la tarea finalice, manteniendo así un tiempo de respuesta rápido para el usuario: ```python # views.py from .tasks import send_welcome_email, generate_report def register_user(request): # ... user creation logic ... # Enqueue the task for background execution send_welcome_email.enqueue( user_email=user.email, username=user.username ) return redirect("dashboard") def export_view(request, report_id): generate_report.enqueue(report_id=report_id, format="csv") return JsonResponse({"status": "processing"}) ``` Django 6.0 incluye un backend de base de datos como opción por defecto para entornos de desarrollo y proyectos con volumen bajo, y un backend de Redis para escenarios de producción con mayor carga. La configuración en `settings.py` permite seleccionar el backend, el número de workers y los intervalos de polling. > **Tareas nativas vs. Celery** > > El sistema nativo de Django 6.0 cubre casos de uso sencillos a moderados de manera efectiva. Para escenarios que requieren enrutamiento avanzado de colas, workflows complejos (chains, chords, groups), reintentos con backoff exponencial configurable, procesamiento distribuido en múltiples máquinas o planificación periódica avanzada, [Celery](/es/blog/django/django-celery-async-tasks-interview-2026) sigue siendo la herramienta adecuada. ## Template partials: fragmentos reutilizables sin proliferación de archivos El sistema de plantillas de Django siempre ofreció la etiqueta `{% include %}` para reutilizar fragmentos de HTML. El problema era que cada fragmento necesitaba su propio archivo, lo que generaba directorios con decenas de archivos pequeños difíciles de navegar y mantener. Los template partials resuelven este problema permitiendo definir múltiples fragmentos dentro de un mismo archivo y referenciarlos de manera selectiva. Las etiquetas `{% partialdef %}` y `{% endpartialdef %}` delimitan cada fragmento con un nombre único. El siguiente ejemplo agrupa dos representaciones visuales de un producto en un solo archivo de componentes: ```html {% partialdef product_card %}
{{ product.price|floatformat:2 }} EUR
{% if product.in_stock %}In Stock{% else %}Sold Out{% endif %}