One of the solutions to update m2m, along with updating one of your models.
Django 1.11 and higher
First of all, all requests via admin panel are atomic. You can look at ModelAdmin:
@csrf_protect_m
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
with transaction.atomic(using=router.db_for_write(self.model)):
return self._changeform_view(request, object_id, form_url, extra_context)
@csrf_protect_m
def delete_view(self, request, object_id, extra_context=None):
with transaction.atomic(using=router.db_for_write(self.model)):
return self._delete_view(request, object_id, extra_context)
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:
The main object is updated.
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)
- 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.