Trigering post_save signal only after transaction has completed

django signals
django post_save
django-transaction-hooks
django post_save update_fields
django signals update
django signals request_started
delete signal in django
django m2m_changed signal

I have written some APIs, for which the respective functions executive inside a transaction block. I am calling the save() method (after some modifications) on instance/s of a/several Model/s, and also consecutively indexing some JSON related information of the instance/s in Elasticsearch. I want the database to rollback even if for some reason the save() for one of the instances or indexing to the Elasticsearch fails.

Now, the problem is arising that even inside the transaction block, the post_save() signals gets called, and that is an issue because some notifications are being triggered from those signals.

Is there a way to trigger post_save() signals only after the transactions have completed successful?

Trigering post_save signal only after transaction has completed, Not really. The signals have nothing to do with the db transaction success or failure, but with the save method itself - before the call you have the pre_save signal  To demo this let’s insert a record into a table that has an after insert trigger from a transaction and call rollback from the trigger. If you’ve read my nested transactions post you’ll know that any ROLLBACK nested or not will end all transactions and that nested transactions are really a lie.

I think the simplest way is to use transaction.on_commit(). Here's an example using the models.Model subclass Photo that will only talk to Elasticsearch once the current transaction is over:

from django.db import transaction
from django.db.models.signals import post_save

@receiver(post_save, sender=Photo)
def save_photo(**kwargs):
    transaction.on_commit(lambda: talk_to_elasticsearch(kwargs['instance']))

Note that if the transaction.on_commit() gets executed while not in an active transaction, it will run right away.

Django Post-save Signal in a Transaction, So let's assume you have this cool Django project that uses signals to achieve as it is not readily obvious where some action is performed after a signal is fired. Now you don't just have a cool code, you have a hipster code! user.​set_password(password) user.save() # post_save is triggered here # do  +1 for this. It would be useful for any kind of after-the commit processing that is too large to be done in-band but even more importantly the 'post save' will become much less of a gotcha because with a 'post commit' people will realize that 'post save' does not occur after a finalized transaction.

I was having serious issues with django's admin not allowing post_save transactions on parent objects when they had inline children being modified.

This was my solution to an error complaining about conducting queries in the middle of an atomic block:

def on_user_post_save_impl(user):
     do_something_to_the_user(user)

def on_user_post_save(sender, instance, **kwargs):
    if not transaction.get_connection().in_atomic_block:
        on_user_post_save_impl(instance)
    else:
        transaction.on_commit(lambda: on_user_post_save_impl(instance))

#30022 (Doc how to combine post_save signal with on_commit to , Trying to alter a many to many relation when saving a model's instance is a performed in save() are committed to the database in the same transaction as the would trigger the post_save receiver would only queue the group additions to be  The signals have nothing to do with the db transaction success or failure, but with the save method itself - before the call you have the pre_save signal fired and after the call you have the post_save signal fired.

#14051 (Signals for transaction commit/rollback) – Django, Some users of django-celery have the problem of publishing references to signals for models, or to change post_* to only fire when a transaction is that is too large to be done in-band but even more importantly the 'post save' will with a middleware in process_exception when ATOMIC_REQUESTS triggers a rollback. If the trigger raises an error, the transaction will be rolled back (you will lose all uncommited changes). You can use DBMS_JOB to defer an action to after the commit. This would be an asynchronous action and may be desirable in some cases (for example when you want to send an email after the transaction has been successful).

Channels may "complete" before a transaction is finished · Issue , Transaction opened A1. item saved -> post_save signal thrown A2. and triggers a search index updater A4. updater queries database for and see Or, you can manage transactions yourself and send messages only after  Provide program and variant name and after providing all the values press save button. In the initial screen press save button. Step4: Now execute the program to trigger the event and as well check the background job. Run transaction SM37 Check the status of job created by the program. Now check the spool to see the generated list

Django Anti-Patterns: Signals, Django's Signal Dispatcher is a really powerful feature that you should never use. maintenance of the code and only learns of the existence of signals when trying to File: signals.py from django.db.models.signals import post_save from Similarly, request_{started,finished} could be accomplished by  After that, we create a trigger named Count_tupples that will increment the value of count after insertion of any new Tupple in the table employee. GeeksforGeeks has prepared a complete interview preparation course with premium videos, theory, practice problems, TA support and many more features. Please refer Placement 100 for details

Comments
  • Thanks, I had already tried the first approach, I was setting instance.in_transaction = True, but that did create a lot of the confusion as many of the other developers then missed the handling in their signals. I think, the second approach will be much more easy to handle, and will be with least complications.
  • This does not work - AttributeError: "'module' object has no attribute 'on_commit'"
  • Have you imported transaction from django? I think it is django.db.transaction