python / expert
Snippet
Django ModelForm Sets with Dynamic Validation and Clean Methods
This snippet demonstrates Django formset validation patterns combining model-level, field-level, and formset-level validation. The `clean()` methods implement cross-field validation, while the standalone `validate_invoice_formset` function enforces business rules across all forms. This multi-layered validation architecture ensures data integrity from individual fields to total invoice amounts.
snippet.py
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from django import formsfrom django.core.exceptions import ValidationErrorfrom django.forms import formset_factoryfrom myapp.models import Invoice, InvoiceItemclass InvoiceItemForm(forms.ModelForm):class Meta:model = InvoiceItemfields = ['description', 'quantity', 'unit_price']def clean_quantity(self):quantity = self.cleaned_data['quantity']if quantity <= 0:raise ValidationError('Quantity must be positive')return quantitydef clean(self):super().clean()if self.cleaned_data.get('quantity') and self.cleaned_data.get('unit_price'):total = self.cleaned_data['quantity'] * self.cleaned_data['unit_price']if total > 100000:raise ValidationError('Line total exceeds maximum allowed value')return self.cleaned_dataclass InvoiceForm(forms.ModelForm):class Meta:model = Invoicefields = ['client_name', 'invoice_date', 'notes']def clean_client_name(self):name = self.cleaned_data['client_name']if len(name) < 3:raise ValidationError('Client name too short')return name.upper()def validate_invoice_formset(formset):if formset.total() == 0:raise ValidationError('At least one invoice item required')if formset.total() > 50:raise ValidationError('Maximum 50 items per invoice allowed')totals = []for form in formset.forms:if form.is_valid():qty = form.cleaned_data.get('quantity', 0)price = form.cleaned_data.get('unit_price', 0)totals.append(qty * price)if totals and sum(totals) > 500000:raise ValidationError('Invoice total exceeds credit limit')InvoiceItemFormSet = formset_factory(InvoiceItemForm, extra=1, can_delete=True)InvoiceFormSet = formset_factory(InvoiceForm)
django
Breakdown
1
def clean_quantity(self)
Field-level validation ensuring positive numeric values
2
def clean(self)
Cross-field validation checking business rules within single form
3
raise ValidationError(...)
Stops validation pipeline and associates error with field or non-field
4
def validate_invoice_formset(formset)
Formset-level validation enforcing rules across all forms
5
formset.total() == 0
Checks minimum formset requirement before processing