[Django]-Django(v2.0.7) Admin: Callback post DB transaction commit

4👍

TLDR: Override change_view


After digging into the source code file django.contrib.admin.option.py, it appears that saving model and related M2M is being triggered by this code within _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)

which is getting called by changeform_view which sets the atomic transaction. This is what I wanted to override so that I can execute publish_event once things are committed to DB:

@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)

This code in turn is called by change_view and 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)

Since I’m making only updates(not create) via form, I overrode change_view to explicitly call 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

As soon as change_resp = super(MySampleModelAdmin, self).change_view(request, object_id, form_url, extra_context) is done with execution, transaction is committed, so it’s safe to call publish_event at this step. After this change_view just expects a response in return.


EDIT:
Tried on_commit, and this seems to work too. This is based on signals.

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))

0👍

transaction.on_commit(lambda: handle_model_after_save(instance.id))

saved my life!

Leave a comment