run code after transaction commit in Django

2019-01-21 20:43发布

Is there any way to run some code after transaction commit in Django?

I need to send some messages to a rabbitmq server for offline processing, but the message gets to the consumer before the Django transaction is commited.

My message is sent in the post_save signal of the model. What I'm looking for is a similar mechanism, using signals or something else, that would execute code after the commit (and do nothing if the transaction fails).

I haven't found any generic way of doing it in Django. Do you have any ideas?

5条回答
我想做一个坏孩纸
2楼-- · 2019-01-21 20:57

Have a look at django-celery-transactions for a solution to this.

I've recently finished splitting-out and refactoring the underlying signals code code into a stand-alone app django-db-signals.

查看更多
Root(大扎)
3楼-- · 2019-01-21 20:58

I have implemented transaction signals (post_commit and post_rollback) by monkey patching django: http://gist.github.com/247844

查看更多
萌系小妹纸
4楼-- · 2019-01-21 21:08

One possibility would be to subclass the transaction middleware so that it sends a custom signal on commit. Your code could listen for that signal, rather than post_save.

查看更多
Root(大扎)
5楼-- · 2019-01-21 21:12

UPDATE 2: django-transaction-hooks was merged into Django core and released in Django version 1.9.

UPDATE: django-transaction-hooks solves this problem.

I don't believe there is a clean way to do this; at least I can't think of one. You could monkeypatch django.db.transaction.commit to send a custom signal; not pretty but I think it would work.

Might also be interesting to raise this use-case on the django-developers mailing list. The devs are generally averse to adding new signals, but you might have a good case here (and a rebuttal from a core dev might include a useful suggestion of how to resolve your situation). You're more likely to get responses if you wait until after 1.1 comes out, though.

查看更多
对你真心纯属浪费
6楼-- · 2019-01-21 21:14

Hope this may help someone using Django 1.9 or later. Since 1.9 on_commit is available.

So basically you would be doing it like this:

from django.db import transaction

transaction.on_commit(
    lambda: send_msg_to_rabbitmqp(param1, param2, ...)
)

If you wish to keep post_save, you can still use on_commit:

@receiver(pre_save, sender=MyModel)
def my_handler(sender, instance, created, **kwargs):
    transaction.on_commit(
        lambda: send_msg_to_rabbitmqp(instance.id)
    )
查看更多
登录 后发表回答