Introduction
Customize formset validation beyond individual form validation.
Key Concepts
- BaseFormSet.clean(): Cross-form validation logic.
- min_num/max_num: Form count limits with validate_min/validate_max.
- self.forms: List of form instances.
Real World Context
A quiz builder requiring 5-50 unique questions. The formset clean() checks duplicates while min_num enforces minimums.
Deep Dive
Custom Formset Class
pythonfrom django.forms import BaseFormSet class BaseBookFormSet(BaseFormSet): def clean(self): if any(self.errors): return # Skip if form errors exist titles = [] for form in self.forms: if form.cleaned_data: title = form.cleaned_data.get('title') if title in titles: raise ValidationError('Duplicate titles not allowed') titles.append(title) BookFormSet = formset_factory( BookForm, formset=BaseBookFormSet, extra=3 )
Min/Max Forms
pythonBookFormSet = formset_factory( BookForm, min_num=1, # Minimum forms required max_num=10, # Maximum forms allowed validate_min=True, validate_max=True, )
Common Pitfalls
- Not checking self.errors first -- Always start with if any(self.errors): return.
- Validating deleted forms -- Skip forms with DELETE flag.
Best Practices
- Combine min_num with validate_min -- min_num alone is display-only.
- Specific error messages -- "Duplicate titles" beats "Invalid formset".
Summary
Override clean() in BaseFormSet for cross-form validation. Use min_num/max_num to enforce limits.