仅在事务完成后才会触发post_save信号

时间:2015-10-16 22:56:39

标签: django api django-models transactions django-signals

我编写了一些API,其中各个函数在事务块中执行。我在一个/多个Model / s的实例上调用save()方法(经过一些修改),并在 Elasticsearch 中连续索引实例的一些JSON相关信息。我希望数据库回滚,即使由于某种原因,其中一个实例的save() Elasticsearch 的索引失败。

现在出现的问题是,即使在事务块内,也会调用post_save()信号,这是一个问题,因为某些通知是从这些信号中触发的。

是否有办法在交易成功完成后触发post_save()信号?

3 个答案:

答案 0 :(得分:4)

不是真的。这些信号与db事务成功或失败无关,但是使用save方法本身 - 在调用之前你已经触发了pre_save信号,在调用之后你发出了post_save信号。

这里有两种方法:

  • 您将在post_save方法中检查实例并确定模型是否已成功保存;最简单的方法:在save方法中,在事务执行成功后,使用一个标记注释你的实例,比如instance.saved_successfully = True,你将在post_save处理程序中测试。
  • 您将抛弃post_save信号并为自己创建一个自定义信号,您将在事务成功运行后触发该信号。

有道理吗?

P.S。

如果您严格需要绑定到事务提交信号,请查看此包:https://django-transaction-hooks.readthedocs.org/en/latest/;看起来该功能已集成在Django 1.9a中。

答案 1 :(得分:4)

我认为最简单的方法是使用transaction.on_commit()。这是一个使用models.Model子类Photo的示例,该子类仅在当前事务结束后才与Elasticsearch对话:

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

请注意,如果transaction.on_commit()在未处于活动事务中时被执行,它将立即运行。

答案 2 :(得分:1)

我在django的管理员中遇到了严重的问题,当他们修改了内联子级时,不允许在父对象上进行post_save事务。

这是我的一个错误解决方案,该错误抱怨在原子块中间进行查询:

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))
相关问题