Search code examples
djangoinline-formsetdynamicform

inlineformset - incrementing TOTAL_FORMS gives MultiValueDictKeyError


I'm trying achieve a dynamic formset without javascript support.

For this, I've added a button at the bottom of the form,

<input type="submit" name="add_item" value="+" id="submit-id-add_item">

Now, inside my CreateView, I have tried the following code.

def get_context_data(self, **kwargs):
    context = super(AddInvoice, self).get_context_data(**kwargs)
    ItemInlineFormSet = inlineformset_factory(Invoice, InvoiceItem, form=InvoiceItemForm, extra=1,  can_delete=False, formset=InvoiceItemFormSet)
    if self.request.method=='POST':
        if 'add_item' in self.request.POST:
            cp = self.request.POST.copy()
            cp['item-TOTAL_FORMS'] = int(cp['item-TOTAL_FORMS'])+ 1
            context['item_formset'] = ItemInlineFormSet(cp,prefix='item')
            else:
                context['item_formset'] = ItemInlineFormSet(self.request.POST,prefix='item')

    else:
        context['item_formset'] = ItemInlineFormSet (prefix='item')
        return context

But, when I click on the add_item button, I get the following error

MultiValueDictKeyError at ... 'item-1-item'

(my form prefix is item and also have a field named item)

I suspect this is because django looks for the next set of form elements to match with TOTAL_FORMS but it can't find anywhere.

What I'm missing here?

Thanks.


Solution

  • I had initially tried something around extra which didn't work - perhaps I overlooked something. Here's the working code.

    ItemInlineFormSet = inlineformset_factory(Invoice, InvoiceItem, 
        form=InvoiceItemForm,
        extra=extra,  can_delete=False,
        formset=InvoiceItemFormSet)
    
    if self.request.method=='POST':
        if 'add_item' in self.request.POST:
            extra= int(self.request.POST.get('item-TOTAL_FORMS'))+ 1
            ItemInlineFormSet = inlineformset_factory(Invoice, InvoiceItem, form=InvoiceItemForm, extra=extra,  can_delete=False,
                                             formset=InvoiceItemFormSet)
            context['item_formset'] = ItemInlineFormSet(prefix='item')
        else:
    
            context['item_formset'] = ItemInlineFormSet(self.request.POST,prefix='item')
    
    else:
        context['item_formset'] = ItemInlineFormSet (prefix='item')
    return context