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.

Django 6.0 chiavi primarie composite e background tasks nel 2026

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
<!-- templates/components/cards.html -->
{% partialdef product_card %}
<div class="card">
    <h3>{{ product.name }}</h3>
    <p class="price">{{ product.price|floatformat:2 }} EUR</p>
    <span class="stock {% if product.in_stock %}available{% else %}sold-out{% endif %}">
        {% if product.in_stock %}In Stock{% else %}Sold Out{% endif %}
    </span>
</div>
{% endpartialdef %}

{% partialdef product_row %}
<tr>
    <td>{{ product.name }}</td>
    <td>{{ product.price|floatformat:2 }} EUR</td>
    <td>{{ product.quantity }}</td>
</tr>
{% endpartialdef %}

L'inclusione selettiva di un partial si effettua tramite la sintassi con il frammento #nome nel tag {% include %}:

html
<!-- templates/shop/catalog.html -->
{% extends "base.html" %}

{% block content %}
<div class="product-grid">
    {% for product in products %}
        {% include "components/cards.html#product_card" %}
    {% endfor %}
</div>
{% 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.

Pronto a superare i tuoi colloqui su Django?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

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 <script> del template. Questo meccanismo consente l'esecuzione degli script inline legittimi bloccando al contempo qualsiasi script iniettato da un aggressore. Il dizionario SECURE_CSP_REPORT_ONLY attiva una modalità di sola segnalazione particolarmente utile durante la fase di implementazione: le violazioni vengono registrate senza bloccare le risorse, permettendo ai team di affinare progressivamente le direttive prima di passare alla modalità di blocco effettivo.

L'integrazione nativa garantisce la compatibilità completa con il motore di template di Django, compresa la generazione automatica degli attributi nonce nei blocchi {% static %} e negli script inline del pannello di amministrazione.

Altre modifiche rilevanti in Django 6.0

Oltre alle funzionalità principali, Django 6.0 introduce diversi cambiamenti che incidono sullo sviluppo quotidiano.

BigAutoField come chiave primaria predefinita. Il valore di DEFAULT_AUTO_FIELD nei nuovi progetti punta ora a BigAutoField (64 bit) anziché ad AutoField (32 bit), eliminando il limite di 2,1 miliardi di record che caratterizzava il vecchio campo autoincrementale. I progetti esistenti che non hanno configurato esplicitamente questo valore riceveranno un avviso durante l'esecuzione delle migrazioni.

Paginazione asincrona. I QuerySet paginati supportano ora metodi asincroni come await page.ahas_next() e await page.ahas_previous(). Le viste asincrone possono così paginare i risultati senza bloccare l'event loop, migliorando il throughput delle applicazioni ASGI.

Auto-import nella shell. Il comando python manage.py shell importa automaticamente tutti i modelli del progetto e i moduli di uso più frequente (django.db.models, django.utils.timezone, datetime). Questa miglioria elimina la scrittura ripetitiva degli import a ogni apertura di sessione interattiva di debug.

API email modernizzata. Il modulo django.core.mail adotta l'API email.message.EmailMessage di Python al posto delle classi MIME legacy, offrendo un supporto nativo per allegati inline, intestazioni personalizzate e un'interfaccia più fluida per la composizione di email complesse.

Domande di colloquio su Django per il 2026

I colloqui tecnici per posizioni backend Python nel 2026 includono con frequenza crescente domande sulle funzionalità recenti di Django, accanto ai fondamentali dell'ORM, del middleware e dei framework REST.

Qual è la differenza tra unique_together e CompositePrimaryKey?

unique_together crea un vincolo di unicità su un insieme di colonne, ma il modello conserva il proprio campo id autoincrementale come chiave primaria. CompositePrimaryKey elimina il campo id e stabilisce la combinazione di colonne come vera chiave primaria a livello di database. Il primo costituisce un vincolo aggiuntivo; il secondo ridefinisce l'identità stessa del record. Sul piano delle prestazioni, la chiave composita evita l'overhead di un indice supplementare e allinea il modello Django allo schema relazionale sottostante.

Quando è preferibile il framework di task nativo di Django 6.0 rispetto a Celery?

Il sistema nativo si adatta a task semplici che non richiedono routing avanzato delle code, meccanismi di retry sofisticati né workflow complessi di tipo canvas (chains, groups, chords). Celery diventa necessario quando l'applicazione richiede l'elaborazione distribuita su più macchine, la pianificazione periodica avanzata con Celery Beat, il monitoraggio in tempo reale con strumenti come Flower, o politiche di backoff esponenziale configurabili.

Come si posiziona il middleware CSP nell'ordine di esecuzione dei middleware Django?

I middleware vengono eseguiti nell'ordine di dichiarazione nella lista MIDDLEWARE durante la fase di richiesta e in ordine inverso durante la fase di risposta. ContentSecurityPolicyMiddleware va posizionato dopo SecurityMiddleware per assicurare che le intestazioni di sicurezza di base siano già presenti. Il middleware CSP agisce nella fase di risposta, iniettando le intestazioni Content-Security-Policy con le direttive configurate in SECURE_CSP e generando automaticamente un nonce univoco per ogni richiesta.

Cosa sono i template partials e quando risultano preferibili rispetto a {% include %} con file separati?

I template partials consentono di definire frammenti nominati all'interno di un singolo file template tramite {% partialdef %}. La sintassi {% include "file.html#nome" %} permette di includere esclusivamente il frammento specificato. Risultano preferibili quando più varianti di un componente sono logicamente correlate e la loro separazione in file distinti genererebbe una frammentazione eccessiva della directory dei template. Per frammenti indipendenti e autonomi, il classico {% include %} con file separati resta l'approccio appropriato.

Migrazione da Django 5.2 a 6.0

Django 6.0 abbandona il supporto per Python 3.10 e 3.11: Python 3.12 o superiore diventa obbligatorio. Il valore predefinito di DEFAULT_AUTO_FIELD cambia in BigAutoField, con possibili avvisi durante le migrazioni dei progetti esistenti. Le classi email utilizzano ora l'API email.message.EmailMessage di Python, con potenziali incompatibilità per il codice che si basa sugli interni MIME legacy. Si raccomanda di eseguire una suite di test completa prima di aggiornare le applicazioni in produzione.

Conclusione

Django 6.0 rappresenta un consolidamento significativo del framework, riducendo la dipendenza da pacchetti esterni per funzionalità che la comunità richiedeva da anni.

  • Le chiavi primarie composite (CompositePrimaryKey) eliminano le colonne surrogate id nelle tabelle di giunzione e nelle tabelle intermedie, allineando l'ORM allo schema relazionale del database
  • Il framework nativo per task in background (@task / .enqueue()) gestisce le operazioni asincrone semplici senza la complessità operativa di Celery e di un broker di messaggi esterno
  • I template partials ({% partialdef %} / {% endpartialdef %}) raggruppano frammenti di template correlati in un singolo file, riducendo la proliferazione di file e integrandosi con pattern HTMX
  • Il middleware CSP nativo (ContentSecurityPolicyMiddleware) fornisce protezione contro attacchi XSS con generazione automatica di nonce e modalità report-only per implementazioni graduali
  • BigAutoField come campo predefinito previene l'esaurimento delle chiavi primarie nelle tabelle ad alto volume senza configurazione aggiuntiva
  • Paginazione asincrona, auto-import nella shell e API email moderna migliorano la produttività quotidiana dello sviluppatore sia nel codice di produzione che nelle sessioni di debug
  • La preparazione ai colloqui dovrebbe coprire le differenze tra unique_together e CompositePrimaryKey, il confronto tra il framework di task nativo e Celery, l'ordine di esecuzione dei middleware e i casi d'uso appropriati per i template partials

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Tag

#django
#python
#composite-primary-keys
#background-tasks
#django-6

Condividi

Articoli correlati