00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Relazioni tra Modelli

Le relazioni tra modelli permettono di collegare tabelle diverse nel database. Django supporta tre tipi di relazione: uno-a-molti (ForeignKey), uno-a-uno (OneToOneField) e molti-a-molti (ManyToManyField).

ForeignKey (Uno-a-Molti)

Una ForeignKey crea una relazione uno-a-molti. Ogni articolo appartiene a una sola categoria, ma una categoria puo avere molti articoli:

from django.db import models

class Categoria(models.Model):
    nome = models.CharField(max_length=100)

    def __str__(self):
        return self.nome

class Articolo(models.Model):
    titolo = models.CharField(max_length=200)
    categoria = models.ForeignKey(
        Categoria,
        on_delete=models.CASCADE,
        related_name='articoli',
    )

Opzioni on_delete

Il parametro on_delete definisce il comportamento quando l’oggetto referenziato viene eliminato:

# CASCADE: elimina anche gli articoli collegati
categoria = models.ForeignKey(Categoria, on_delete=models.CASCADE)

# PROTECT: impedisce l'eliminazione se ci sono oggetti collegati
categoria = models.ForeignKey(Categoria, on_delete=models.PROTECT)

# SET_NULL: imposta il campo a NULL (richiede null=True)
categoria = models.ForeignKey(Categoria, on_delete=models.SET_NULL, null=True)

# SET_DEFAULT: imposta il campo al valore predefinito
categoria = models.ForeignKey(Categoria, on_delete=models.SET_DEFAULT, default=1)

# DO_NOTHING: non fa nulla (puo causare errori di integrita)
categoria = models.ForeignKey(Categoria, on_delete=models.DO_NOTHING)

OneToOneField (Uno-a-Uno)

Crea una relazione uno-a-uno, utile per estendere un modello esistente:

from django.contrib.auth.models import User

class Profilo(models.Model):
    utente = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        related_name='profilo',
    )
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)

# Utilizzo
utente = User.objects.get(username='mario')
print(utente.profilo.bio)  # accesso tramite related_name

ManyToManyField (Molti-a-Molti)

Crea una relazione molti-a-molti. Django genera automaticamente una tabella intermedia:

class Tag(models.Model):
    nome = models.CharField(max_length=50, unique=True)

class Articolo(models.Model):
    titolo = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag, related_name='articoli', blank=True)
# Gestire la relazione
articolo = Articolo.objects.get(pk=1)
tag_python = Tag.objects.get(nome='python')

articolo.tags.add(tag_python)              # aggiungere
articolo.tags.remove(tag_python)           # rimuovere
articolo.tags.set([tag1, tag2, tag3])      # impostare la lista completa
articolo.tags.clear()                       # rimuovere tutti
articolo.tags.all()                         # tutti i tag dell'articolo

Modello Through (Tabella Intermedia Personalizzata)

Per aggiungere campi extra alla relazione molti-a-molti, usa un modello through:

class Studente(models.Model):
    nome = models.CharField(max_length=100)
    corsi = models.ManyToManyField('Corso', through='Iscrizione')

class Corso(models.Model):
    nome = models.CharField(max_length=200)

class Iscrizione(models.Model):
    studente = models.ForeignKey(Studente, on_delete=models.CASCADE)
    corso = models.ForeignKey(Corso, on_delete=models.CASCADE)
    data_iscrizione = models.DateField(auto_now_add=True)
    voto_finale = models.DecimalField(max_digits=4, decimal_places=2, null=True)

    class Meta:
        unique_together = ['studente', 'corso']
# Con through, usa il modello intermedio per creare relazioni
Iscrizione.objects.create(studente=mario, corso=django_corso, voto_finale=28.5)

Relazioni Inverse (related_name)

Il parametro related_name definisce il nome per accedere alla relazione inversa:

# Dal lato "uno" verso "molti"
categoria = Categoria.objects.get(pk=1)
articoli_categoria = categoria.articoli.all()  # usa il related_name

# Filtrare tramite relazione inversa
categorie_con_articoli = Categoria.objects.filter(articoli__pubblicato=True)

Ottimizzazione con select_related e prefetch_related

Le relazioni possono causare il problema N+1 query. Django offre due metodi per risolverlo:

# select_related: per ForeignKey e OneToOneField (usa JOIN SQL)
articoli = Articolo.objects.select_related('categoria', 'autore').all()
for articolo in articoli:
    print(articolo.categoria.nome)  # nessuna query aggiuntiva

# prefetch_related: per ManyToManyField e relazioni inverse (usa query separate)
articoli = Articolo.objects.prefetch_related('tags').all()
for articolo in articoli:
    print(articolo.tags.all())  # nessuna query aggiuntiva

# Combinazione di entrambi
articoli = Articolo.objects.select_related('categoria').prefetch_related('tags').all()

select_related esegue un JOIN SQL ed e ideale per relazioni singole. prefetch_related esegue query separate ed e ideale per relazioni molti-a-molti e inverse.

Conclusione

Le relazioni tra modelli sono fondamentali per progettare database relazionali in Django. ForeignKey, OneToOneField e ManyToManyField coprono tutti i tipi di relazione, mentre related_name, i modelli through e le ottimizzazioni con select_related e prefetch_related ti permettono di gestirle in modo efficiente e leggibile.