Trigering post_save signal only after transaction has completed

前端 未结 3 718
旧巷少年郎
旧巷少年郎 2020-12-30 02:27

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

相关标签:
3条回答
  • 2020-12-30 03:04

    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 fired and after the call you have the post_save signal fired.

    There are 2 approaches here:

    • you are going to inspect the instance in the post_save method and decide that the model was saved successfully or not; simplest way to do that: in the save method, after the transaction executed successfully, annotate your instance with a flag, say instance.saved_successfully = True, which you will test in the post_save handler.
    • you are going to ditch the post_save signal and create a custom signal for yourself, which you will trigger after the transaction ran successfully.

    Makes sense?

    P.S.

    If you strictly need to bind to the transaction commit signal, have a look over this package: https://django-transaction-hooks.readthedocs.org/en/latest/; it looks like the functionality is integrated in Django 1.9a.

    0 讨论(0)
  • 2020-12-30 03:18

    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.

    0 讨论(0)
  • 2020-12-30 03:26

    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))
    
    0 讨论(0)
提交回复
热议问题