Search code examples
djangotransactionsinline-formset

Django: Saving new object with form and inline formset (transaction managment)


I have a form in which I creates a new "parent" object, exactly one child object of type A and n child objects of type B (n can also be 0). I do this with a form and and two inlineformsets.

I have set in settings 'ATOMIC_REQUESTS': True.

I'm using a function based view. The code for saving the data is like this:

if form.is_valid():      
    parent = form.save();
    formsetA = AFormset(request.POST, instance=parent)
    formsetB = BFormset(request.POST, instance=parente)
    if formsetA.is_valid():
        # save first form in inline formset (there always only is one)
        # and then assign meta data
        a = formset[0].save()
        if formsetB.is_valid():
            for b in formsetB:
                if b.is_valid():
                    b.save()

Check if parent is valid, save it and set it as instance of the inline formsets. Then check the inline formsets. If a form is not valid I redirect back to the form. No exception.

Issue is that if a inlineformset is invalid, it returns to the form but the parent was already saved and transaction is not rolled back. If I roll back the transaction manually just before redirecting back to the form I get an exception:

An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

I assume I can't manually rollback inside atomic block?

My main question is how I should handle this situation correctly, eg. saving parent and child at once?


Solution

  • What solved the immediated issue is to not commit on parent form save and then first check formsets for validity before committing the parent:

    if form.is_valid():      
        parent = form.save(commit=False);
        formsetA = AFormset(request.POST, instance=parent)
        formsetB = BFormset(request.POST, instance=parente)
        if formsetA.is_valid() and formsetB.is_valid():
            #formsets are valid, parent can be saved
            # this assumes if formset is valid, all contained forms are too
            parent.save()
            a = formset[0].save()
            for b in formsetB:                    
                b.save()