user
user

Reputation: 445

Django - inline formset validation

I'm trying to validate my inline formset cost value's to make sure that it adds up to 100. The formset is returned 5 times so foreach the value should be added until 100 is hit. If it's more or less than that show an error & not allow the user to hit the create button. I'm trying to validated all the forms combined not each form value.

Models.py

class EstimatedBudgetForm(forms.ModelForm):
def clean(self):
    # get forms that actually have valid data
    count = 0
    for percentage in self.cost:
        try:
            if percentage.cleaned_data:
                count += percentage
        except AttributeError:
            # annoyingly, if a subform is invalid Django explicity raises
            # an AttributeError for cleaned_data
            pass
    if count != 100:
        raise forms.ValidationError('Percentage must equal 100%')

Views.py

EstimatedBudgetChildFormset = inlineformset_factory(
  Project, EstimatedBudget, fields=('project', 'item', 'cost', 'time'), can_delete=False, form=EstimatedBudgetForm, extra=5, widgets={'item': forms.Select(attrs={'disabled': True})},
)

Upvotes: 3

Views: 640

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21807

To perform validation for a formset one should override the clean method of the formset (not the form). For an inline formset you need to inherit from BaseInlineFormSet and use that (For more information see Overriding clean() on a ModelFormSet [Django docs]):

from django.forms import BaseInlineFormSet


class MyInlineFormset(BaseInlineFormSet):
    def clean(self):
        super().clean()
        count = 0
        for form in self.forms:
            if not form.errors: # No need for a try-except, just check if the form doesn't have errors
                count += form.cleaned_data['cost']
        if count != 100: # This condition might not work if you are dealing with floating point numbers
            raise forms.ValidationError('Percentage must equal 100%')

Next while making the formset, specify the formset kwarg rather than form:

EstimatedBudgetChildFormset = inlineformset_factory(
  Project, EstimatedBudget, fields=('project', 'item', 'cost', 'time'), can_delete=False, formset=MyInlineFormset, extra=5, widgets={'item': forms.Select(attrs={'disabled': True})},
)

Upvotes: 6

Related Questions