Model Formset validation

I’m working with a model formset. this is my form:

class UnloadForm(forms.ModelForm):
    class Meta:
        model = Unload
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['fieldContract'].queryset = FieldContract.objects.filter(applicativeContract__contract__is_active=True).order_by('applicativeContract__contract__internal_code', 'applicativeContract__internal_code', 'internal_code')
        self.fields['product'].queryset = Product.objects.all().order_by('name')
class BaseUnloadFormset(BaseModelFormSet):
    def clean(self):
        super().clean()

        for form in self.forms:
            cd = form.cleaned_data
            whouse = cd['whouse']
            company = cd['company']
            product = cd['product']
            quantity = cd['quantity']
            movement_date = cd['movement_date']
            helper = StockHelper(product, whouse, company, movement_date)
            if quantity > helper.stock:
                raise ValidationError(f'Attenzione quantità massima disponibile di {product}: {helper.stock}')
            return cd
UnloadFormSet = modelformset_factory(
    model = Unload,
    fields = '__all__',
    extra = 5,
    form = UnloadForm,
    formset=BaseUnloadFormset
)
class UnloadFormSetHelper(FormHelper):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_tag = False
        self.layout = Layout(
            Row(
                'whouse',
                'fieldContract',
                'company',
                'product',
                'quantity',
                'movement_date',
            )
        )
        self.render_required_fields = True

the problem occurs, when I’ve added the clean method in the BaseUnloadFormset.
Since all fields in the model are required, for example not stating the movement_date would prompt a front-end validation.
Unfortunately, although the source code of the web page shows that the field is actually required, forgetting the movement_date would send the form to the back-end and eventually the system would crash since there is no movement_date in the cleaned_data dictonary, thus raising a KeyError.

I tried to follow the Docs for overrinding the clean method:
https://docs.djangoproject.com/en/3.2/topics/forms/modelforms/#overriding-clean-on-a-modelformset

Removing the clean method in the form, would restore the correct behavior.

just in case it could be help full, this is my view:

def unload(request):
    helper = UnloadFormSetHelper()
    context = {
        'helper': helper,
        'title': 'Nuovo SCarico',
    }
    if request.method == 'GET':
        formset = UnloadFormSet(queryset=Unload.objects.none())
        load_user_companies_in_formset(request, formset)
        context['formset'] = formset

    elif request.method == 'POST':
        formset = UnloadFormSet(request.POST)
        context['formset'] = formset
        load_user_companies_in_formset(request, formset)
        if formset.is_valid():
            formset.save()
            messages.success(request, 'Scarico salvato correttamente', fail_silently=True)
            return HttpResponseRedirect(reverse('warehouse:dashboard'))
        else:
            return render(request, 'warehouse/unload.html', context)

    return render(request, 'warehouse/unload.html', context)

I looked for help on other forums but I got no replies. Although it’s a minor thing, since the users know about this already, still it’s not how it is supposed to work and I would like to correct it.

Thank you very much for any tip.

Your return cd statement is inside your for loop. That means you’re going to leave your clean function after running the test on the first entry. If there are multiple entries, they won’t be processed.

I see where you reference the link for overriding clean on a modelformset. Pay attention to the block of text between the two examples in that section:

Also note that by the time you reach this step, individual model instances have already been created for each Form . Modifying a value in form.cleaned_data is not sufficient to affect the saved value. If you wish to modify a value in ModelFormSet.clean() you must modify form.instance

(Also see that in both examples, neither example tries to return a value from the loop.)

:slight_smile: Thank you very much Ken.
I remember, when I started my journey with django by looking at tutorials and docs, that a clean method in a form must always return the clean_data…I guess I was going in autopilot and added without it would break the validation process.