TLDR: переопределить change_view
После копания в файл исходного кода django.contrib.admin.option.py
оказывается, что сохранение модели и связанного с ней M2M запускается этим кодом в _changeform_view
:
if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, not add)
self.save_related(request, form, formsets, not add)
change_message = self.construct_change_message(request, form, formsets, add)
, который вызывается changeform_view
, который устанавливает атомарную транзакцию.Это то, что я хотел переопределить, чтобы я мог выполнить publish_event
после того, как что-то зафиксировано в БД:
@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)
Этот код в свою очередь вызывается change_view
и add_view
.
def change_view(self, request, object_id, form_url='', extra_context=None):
return self.changeform_view(request, object_id, form_url, extra_context)
Поскольку я делаю только обновления (но не создаю) с помощью формы, я переопределил change_view
, чтобы явно вызвать publish_event
:
def change_view(self, request, object_id, form_url='', extra_context=None):
change_resp = super(MySampleModelAdmin, self).change_view(request, object_id, form_url, extra_context)
if request.method != 'GET': # since GET also call this and we don't want event published on GET
publish_event()
return change_resp
Как только change_resp = super(MySampleModelAdmin, self).change_view(request, object_id, form_url, extra_context)
выполнено с выполнением, транзакция зафиксирована, поэтому на этом шаге можно безопасно вызывать publish_event
.После этого change_view
просто ожидает ответ в ответ.
РЕДАКТИРОВАТЬ : пробовал on_commit , и это, похоже, тоже работает.Это основано на сигналах.
from django.db import transaction
@receiver(post_save, sender='app.MySampleModel')
def send_model_save_event(sender, instance=None, created=False, **kwargs):
if instance is None:
log.info('Instance is Null')
return
transaction.on_commit(lambda: handle_model_after_save(instance.id))