Kornikopic
Kornikopic

Reputation: 188

Integrity error when trying to save twice the same object using save_formset

I am out of ideas and I need some help about a problem that I encounter when I try to save the same object. Let me explain.

I have a model, let's call it Pizza. And Topping which has a foreign key to Pizza:

I have implemented an Admin view PizzaAdmin and I have added a tabular inline of Topping

so from PizzaAdmin, I can add several toppings.

I have override the save_formset method where I call a helper to perform a third-party API call. From the API response, I need to save some data to my toppings.

def save_formset(self, request, form, formset, change):
    super(PizzaAdmin, self).save_formset(request, form, formset, change)

    helper = ApiHelper(instance=form.instance)
    helper.sync()

This helper makes a call to an API, parse the response and associate an ID for each toppings of Pizza.

class ApiHelper(...):
    def sync(self):
        # parse response

        for topping in self.instance.toppings.all():
            if ....:
                topping.some_id = ...
                topping.save()

When I reach the topping.save(), I got this error: IntegrityError

I do not want to create a new Topping but update its property some_id.

After futher investigation, I realised that save_formset is called by save_related which is called by changeform_view which as the decorator @transaction.atomic

Is there a way to force the commit inside an atomic transaction ?

Please advise. Thanks a lot.

*** UPDATE: 2017-03-30 ***

I have found transaction.on_commit() which allows me to perform actions after the commit:

class PizzaAdmin(admin.ModelAdmin)
    def save_formset(self, request, form, formset, change):
        super(PizzaAdmin, self).save_formset(request, form, formset, change)    
        transaction.on_commit(lambda: sync_toppings(instance=form.instance))


def sync_toppings(instance):
    helper = ApiHelper(instance=form.instance)
    helper.sync()

Inside sync() method, I have replace my queryset using this logic:

toppings_qs = Topping.objects.filter(pizza=self.instance)

I still have an IntegrityError when I try to update the topping through the queryset.

I do not understand why. Please help.

Upvotes: 0

Views: 251

Answers (1)

Kornikopic
Kornikopic

Reputation: 188

I have found my mistake!!! This is what I did:

def save(self, **kwargs):
    self.is_negative = True
    super(Topping, self).save(self, **kwargs)

Notice the self in save of the super() :(

My fix:

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
    self.is_negative = True
    super(Topping, self).save(force_insert, force_update, using, update_fields)

Upvotes: 0

Related Questions