# Django 6.0 nel 2026: Chiavi Primarie Composite, Background Tasks e Domande da Colloquio > Analisi tecnica approfondita di Django 6.0: chiavi primarie composite con CompositePrimaryKey, framework nativo per task in background, template partials, middleware CSP integrato e domande da colloquio per sviluppatori Python nel 2026. - Published: 2026-06-09 - Updated: 2026-06-09 - Author: SharpSkill - Tags: django, python, composite-primary-keys, background-tasks, django-6 - Reading time: 8 min --- Django 6.0, rilasciato a dicembre 2025, segna una tappa fondamentale nell'evoluzione del framework web Python. Questa versione maggiore consolida le chiavi primarie composite introdotte in Django 5.2, aggiunge un framework nativo per l'esecuzione di task in background, introduce i template partials e integra un middleware dedicato alle Content Security Policy direttamente nel core. Il risultato complessivo riduce in modo significativo la dipendenza da pacchetti di terze parti e semplifica l'architettura delle applicazioni Django destinate alla produzione. > **Cronologia delle versioni Django** > > Django segue un ciclo di rilascio prevedibile con una nuova versione maggiore ogni otto mesi circa. Django 5.2 LTS (aprile 2025) ha introdotto le chiavi primarie composite in forma stabile. Django 6.0 (dicembre 2025) le integra pienamente con il resto dell'ORM, aggiungendo il sistema di task in background, i template partials e il middleware CSP. Python 3.12 o superiore risulta obbligatorio. ## Chiavi primarie composite con CompositePrimaryKey I database relazionali utilizzano le chiavi primarie composite per identificare in modo univoco un record attraverso la combinazione di due o più colonne. Per diciotto anni, Django ha imposto a ogni modello l'utilizzo di una chiave primaria a colonna singola, costringendo gli sviluppatori a ricorrere a soluzioni come `unique_together` o pacchetti esterni. La classe `CompositePrimaryKey`, introdotta in Django 5.2 e pienamente integrata nell'ORM con Django 6.0, risolve questa limitazione storica. Il caso d'uso più frequente riguarda le tabelle intermedie dove la combinazione di due chiavi esterne costituisce l'identità univoca del record. L'esempio seguente modella un inventario nel quale ciascun prodotto in ciascun magazzino possiede una quantità associata: ```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) ``` Il campo `pk` riceve i nomi delle colonne sottostanti nel database, non i nomi dei campi del modello Django. La definizione genera un vincolo `PRIMARY KEY (product_id, warehouse_id)` a livello di database, garantendo l'unicità senza la necessità di una colonna `id` autoincrementale. L'overhead di archiviazione si riduce e le prestazioni delle query sugli indici compositi migliorano sensibilmente rispetto all'approccio con chiave surrogata. ### Interrogazione delle chiavi composite nell'ORM L'interazione con l'ORM mantiene l'interfaccia abituale. La chiave primaria composita viene rappresentata come tupla Python, consentendo operazioni di filtraggio, creazione e assegnazione attraverso questa interfaccia: ```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" ``` Questa funzionalità si rivela particolarmente preziosa per i team che lavorano con schemi di database preesistenti o che migrano da altri ORM dove le chiavi composite erano già supportate. Tre limitazioni meritano attenzione: `ForeignKey` non può riferirsi a un modello con chiave primaria composita (occorre ricorrere a `ForeignObject`), il pannello di amministrazione di Django non supporta ancora pienamente i modelli con chiave composita, e la migrazione fra chiavi singole e composite su tabelle esistenti richiede SQL manuale. ## Framework integrato per Background Tasks in Django 6.0 Prima di Django 6.0, eseguire codice al di fuori del ciclo richiesta-risposta HTTP richiedeva invariabilmente Celery, Django-Q o sistemi di code di terze parti analoghi. Django 6.0 include un framework nativo per i task che gestisce la definizione e l'accodamento delle operazioni asincrone senza dipendenze esterne. Il decoratore `@task` contrassegna qualsiasi funzione Python come accodabile. La chiamata al metodo `.enqueue()` sulla funzione decorata la invia a un backend configurato per l'esecuzione asincrona. Il framework separa la definizione del task dalla sua esecuzione: Django gestisce la serializzazione e la coda, mentre un processo worker separato si occupa dell'elaborazione effettiva. ```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 ``` Il dispatch dai controller avviene tramite il metodo `.enqueue()`, che serializza gli argomenti e li trasmette al backend per l'esecuzione differita: ```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"}) ``` La configurazione `TASKS` in `settings.py` definisce il backend da utilizzare. Django 6.0 fornisce un backend basato su database per lo sviluppo e un backend Redis per scenari a volume più elevato. I deployment in produzione possono sfruttare backend personalizzati collegati a message broker specifici. > **Task in background nativi vs Celery** > > Il framework nativo di Django 6.0 per i task copre i casi d'uso più comuni: operazioni fire-and-forget, invio di email, generazione di report e chiamate verso API esterne. Per funzionalità avanzate come il concatenamento dei task (chains, groups, chords), la pianificazione periodica con Celery Beat, il rate limiting configurabile, il monitoraggio in tempo reale con Flower o la distribuzione del carico su worker multipli, Celery resta la scelta consolidata. ## Template Partials per frammenti riutilizzabili Il motore di template di Django acquisisce con la versione 6.0 una funzionalità che gli sviluppatori di framework frontend considerano ormai scontata: la possibilità di definire frammenti riutilizzabili all'interno di un singolo file e di referenziarli in modo selettivo. I tag `{% partialdef %}` e `{% endpartialdef %}` consentono di raggruppare più componenti visivi nello stesso template e di consumare esclusivamente il frammento necessario. L'esempio seguente definisce due rappresentazioni di un prodotto -- una scheda per le viste catalogo e una riga per le tabelle di inventario -- all'interno dello stesso file: ```html {% partialdef product_card %}

{{ product.name }}

{{ product.price|floatformat:2 }} EUR

{% if product.in_stock %}In Stock{% else %}Sold Out{% endif %}
{% endpartialdef %} {% partialdef product_row %} {{ product.name }} {{ product.price|floatformat:2 }} EUR {{ product.quantity }} {% endpartialdef %} ``` L'inclusione selettiva di un partial si effettua tramite la sintassi con il frammento `#nome` nel tag `{% include %}`: ```html {% extends "base.html" %} {% block content %}
{% for product in products %} {% include "components/cards.html#product_card" %} {% endfor %}
{% endblock %} ``` Il vantaggio principale risiede nella riduzione della proliferazione di file. Prima dei template partials, ogni variazione visiva di un componente richiedeva un file dedicato. Con questa funzionalità, le varianti correlate coesistono nello stesso file, semplificando la manutenzione e la navigazione nel codice. L'approccio si integra naturalmente con HTMX e altri pattern di scambio parziale di HTML, dove il server deve restituire frammenti specifici di una pagina senza eseguire il rendering completo del template. ## Middleware nativo per Content Security Policy La protezione contro gli attacchi di cross-site scripting (XSS) e di iniezione di contenuti richiede intestazioni Content Security Policy configurate correttamente. Django 6.0 integra un middleware dedicato e un modulo `django.utils.csp` che eliminano la necessità di pacchetti esterni come `django-csp`. La configurazione si definisce in `settings.py` attraverso un dizionario che associa le direttive CSP ai rispettivi valori autorizzati: ```python # settings.py from django.utils.csp import CSP MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.middleware.security.ContentSecurityPolicyMiddleware", # ... other middleware ] SECURE_CSP = { "default-src": [CSP.SELF], "script-src": [CSP.SELF, CSP.NONCE], "style-src": [CSP.SELF, "https://fonts.googleapis.com"], "img-src": [CSP.SELF, "https:", "data:"], "font-src": [CSP.SELF, "https://fonts.gstatic.com"], "connect-src": [CSP.SELF], } # Report-only mode for testing (does not block, only reports violations) SECURE_CSP_REPORT_ONLY = { "default-src": [CSP.SELF], "report-uri": ["/csp-report/"], } ``` La costante `CSP.NONCE` genera automaticamente un valore univoco per ogni richiesta HTTP, che viene iniettato nei tag `