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

Formset

I Formset di Django permettono di gestire piu istanze dello stesso form in una singola pagina. Sono particolarmente utili quando devi creare o modificare piu oggetti contemporaneamente, come righe di un ordine o una lista di contatti.

formset_factory

La funzione formset_factory crea un formset a partire da un form standard:

from django import forms
from django.forms import formset_factory

class IngredienteForm(forms.Form):
    nome = forms.CharField(max_length=100)
    quantita = forms.CharField(max_length=50)

IngredienteFormSet = formset_factory(
    IngredienteForm,
    extra=3,       # numero di form vuoti da mostrare
    max_num=10,    # numero massimo di form
    min_num=1,     # numero minimo di form
)

Usare un Formset nella View

La gestione di un formset nella view segue lo stesso schema dei form singoli:

from django.shortcuts import render, redirect

def aggiungi_ingredienti(request):
    if request.method == 'POST':
        formset = IngredienteFormSet(request.POST)
        if formset.is_valid():
            for form in formset:
                if form.cleaned_data:
                    nome = form.cleaned_data['nome']
                    quantita = form.cleaned_data['quantita']
                    # Salva ogni ingrediente
            return redirect('lista_ingredienti')
    else:
        formset = IngredienteFormSet()

    return render(request, 'ingredienti.html', {'formset': formset})

Nel template, il formset si renderizza cosi:

# <form method="post">
#     {% csrf_token %}
#     {{ formset.management_form }}
#     {% for form in formset %}
#         <div class="ingrediente">
#             {{ form.as_p }}
#         </div>
#     {% endfor %}
#     <button type="submit">Salva</button>
# </form>

Il management_form e obbligatorio: contiene i campi nascosti necessari per gestire il formset.

modelformset_factory

La funzione modelformset_factory crea un formset collegato a un modello del database:

from django.forms import modelformset_factory
from .models import Prodotto

ProdottoFormSet = modelformset_factory(
    Prodotto,
    fields=['nome', 'prezzo', 'disponibile'],
    extra=2,
    can_delete=True,  # aggiunge un checkbox per eliminare
)

Nella view, il formset carica automaticamente i dati dal database:

def gestisci_prodotti(request):
    if request.method == 'POST':
        formset = ProdottoFormSet(request.POST)
        if formset.is_valid():
            formset.save()
            return redirect('lista_prodotti')
    else:
        formset = ProdottoFormSet(queryset=Prodotto.objects.filter(disponibile=True))

    return render(request, 'prodotti.html', {'formset': formset})

Il parametro queryset permette di filtrare quali oggetti mostrare nel formset.

inlineformset_factory

La funzione inlineformset_factory e perfetta per modelli con relazione ForeignKey. Permette di gestire gli oggetti correlati direttamente dalla pagina del modello padre:

from django.forms import inlineformset_factory
from .models import Ordine, RigaOrdine

RigaOrdineFormSet = inlineformset_factory(
    Ordine,          # modello padre
    RigaOrdine,      # modello figlio
    fields=['prodotto', 'quantita', 'prezzo_unitario'],
    extra=3,
    can_delete=True,
)
from django.shortcuts import get_object_or_404

def modifica_ordine(request, pk):
    ordine = get_object_or_404(Ordine, pk=pk)

    if request.method == 'POST':
        formset = RigaOrdineFormSet(request.POST, instance=ordine)
        if formset.is_valid():
            formset.save()
            return redirect('dettaglio_ordine', pk=ordine.pk)
    else:
        formset = RigaOrdineFormSet(instance=ordine)

    return render(request, 'modifica_ordine.html', {
        'ordine': ordine,
        'formset': formset,
    })

Il parametro instance collega il formset all’oggetto padre.

Validazione del Formset

Puoi personalizzare la validazione di un formset creando una classe personalizzata:

from django.forms import BaseFormSet, formset_factory, ValidationError

class BaseIngredienteFormSet(BaseFormSet):
    def clean(self):
        super().clean()
        nomi = []
        for form in self.forms:
            if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
                nome = form.cleaned_data.get('nome')
                if nome in nomi:
                    raise ValidationError("Non puoi inserire ingredienti duplicati.")
                nomi.append(nome)

IngredienteFormSet = formset_factory(
    IngredienteForm,
    formset=BaseIngredienteFormSet,
    extra=3,
)

Dati Iniziali

Puoi passare dati iniziali a un formset non collegato a un modello:

def aggiungi_ingredienti(request):
    dati_iniziali = [
        {'nome': 'Farina', 'quantita': '500g'},
        {'nome': 'Zucchero', 'quantita': '200g'},
    ]
    formset = IngredienteFormSet(initial=dati_iniziali)
    return render(request, 'ingredienti.html', {'formset': formset})

Conclusione

I Formset sono uno strumento indispensabile quando devi gestire piu form dello stesso tipo in una pagina. Con formset_factory per form generici, modelformset_factory per modelli e inlineformset_factory per relazioni padre-figlio, Django copre tutti gli scenari piu comuni nella gestione di dati multipli.