Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override Django formset delete behavior: Delete form instance if empty field in Django model formset

Tags:

django

formset

I run into this often:

I want to hide the default delete box in formsets and delete the instance of an object if a certain field is cleared in the each form of the formset.

The typical problem is that, either validation gets in the way or this breaks the blank form detection and starts adding all forms ( even blank extra ) when the formset is saved.

like image 700
Paul Kenjora Avatar asked Jun 25 '13 17:06

Paul Kenjora


2 Answers

Here is the solution I found:

This code creates a model formset, ensures it validates by permitting a blank field, and then on save determines which objects to delete and which forms to save.

TaskFormset = inlineformset_factory(User, FAQ, extra=3, can_delete=False, exclude=('user', 'answer',))
formset = TaskFormset(request.POST, request.FILES, instance=user)
for form in formset:
    form.fields['question'].required = False

// later when performing the formset save

for form in formset:
    if form.instance.pk and form.cleaned_data['question'].strip() == '':
        form.instance.delete()
    elif form.cleaned_data:
        form.save()
like image 136
Paul Kenjora Avatar answered Nov 01 '22 00:11

Paul Kenjora


There's a method on BaseFormSet called _should_delete_form which makes it easy to automatically delete an instance based on your own criteria, however it's a "private" method so not sure about future support. You also need to save the formsets by calling formset.save().

In the example below it will delete a row if all the fields values evaluate to false values.

class MyFormSet(forms.BaseInlineFormSet):
    def _should_delete_form(self, form):
        """Return whether or not the form should be deleted."""
        if form.cleaned_data.get(forms.formsets.DELETION_FIELD_NAME):
            return True  # marked for delete
        fields = ('name', 'question', 'amount', 'measure', 'comment')
        if not any(form.cleaned_data[i] for i in fields):
            return True
        return False
like image 32
Tim Tisdall Avatar answered Nov 01 '22 01:11

Tim Tisdall