ManyToMany field not saved when using Django admin

泪湿孤枕 提交于 2019-12-05 01:05:36

So it turns out the above was not the correct way to implement it. The code belonged in StoreAdmin, by overriding model_save().

This is how I solved it:

class StoreAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        if obj.copy_holidays_from:
            form.cleaned_data['holidays'] = obj.copy_holidays_from.holidays.all()

        super(StoreAdmin, self).save_model(request, obj, form, change)

I probably ran into this same behaviour just today and yes, you are correct in assuming it's related to how django handles the data.

The django admin makes the changes to a ManyToMany field separately from changing the actual object. (Remember that the m2m is saved in a different database table).

In my case if I didn't select anything in the ManyToMany field in the admin site, this would translate into a clear()-operation on the ManyToMany relation. Everything you do in the save()-method is immediately removed by this clear. Same thing with stuff I did in the post_save signal handler.

The solution (for me) was to separate the ManyToMany-field into an inline so it doesn't automatically get saved as empty when modifying the object.

In django 2,1,4 my solution was to use save_related()

def save_related(self, request, form, formsets, change):
    super().save_related(request, form, formsets, change)
    form.instance.permissions.add(request.user)

For me the problem that the admin was only saving the last selected instance of the many fields (last 'holiday' selected). So I had to override the save_model method such as this:

@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):

    def save_model(self, request, obj, form, change):
        form.cleaned_data['holidays'] = StoreHoliday.objects.filter(pk__in=dict(request.POST).get('holidays'))
        super(StoreAdmin, self).save_model(request, obj, form, change)

I spent a lot of time on it and other solutions were not working, so I hope it will help.

One of the solutions to update m2m, along with updating one of your models.

Django 1.11 and higher

The behavior which you can observe during updating, when changes which you made with m2m records were not saved, even after you made them in a save method one of your models or in a signal, happens only because m2m form rewrites all records after the main object is updated.

This is why, step by step:

  1. The main object is updated.

  2. Your code(in a save method or in a signal) made changes (you can look at them, just put a breakpoint in ModelAdmin):

 def save_related(self, request, form, formsets, change):
     breakpoint()
     form.save_m2m()
     for formset in formsets:
         self.save_formset(request, form, formset, change=change)
  1. form.save_m2m() takes all m2m values which were placed on a page(roughly speaking) and replace all m2m records via a related manager. That's why you can't see your changes at the end of a transaction.

There is a solution: make your changes with m2m via transaction.on_commit. transaction.on_commit will make your changes after form.save_m2m() when the transaction is committed.

Unfortunately, the downside of this solution - your changes with m2m will be executed in a separate transaction.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!