为什么我可以在它的post_save信号期间访问一个对象,但是当我触发该信号中的代码时,它会在另一个进程上调用它

mkl*_*ber 14 python django django-signals

总之,我对django信号有一个问题.

我有一个模型为了加快页面加载的响应速度,我正在卸载必须完成的一些密集处理,通过调用我们正在运行的第二个localhost网络服务器,两者都使用相同的数据库.我看到调用进程可以检索对象的行为,但被调用的进程不能.端口80和端口[端口]都指向运行在同一数据库中的django进程.

在models.py中

class A(models.Model):
    stuff...

def trigger_on_post_save( sender, instance, create, raw, **keywords):
    #This line works
    A.objects.get( pk=instance.pk )
    #then we call this
    urlopen( r'http://127.0.0.1:[port]' + 
        reverse(some_view_url, args(instance_pk) ).read()

post_save.connect( trigger_on_post_save, A )
Run Code Online (Sandbox Code Playgroud)

在views.py中

def some_view_function( request, a_pk ):
    #This line raises an object_not_found exception
    A.objects.get( pk=a_pk )
Run Code Online (Sandbox Code Playgroud)

此外,在urlopen调用引发异常之后,该对象在数据库中不存在.据我所知,在保存对象后调用了post_save,并将其写入数据库.这是不正确的?

Yoa*_*Gil 16

我们遇到了类似的问题,最后我们使用了on_commit回调(注意:这只能用于Django> = 1.9).所以,你可以这样做:

from django.db import transaction

class A(models.Model):
    stuff...

def trigger_on_post_save( sender, instance, create, raw, **keywords):
    def on_commit():
        urlopen(r'http://127.0.0.1:[port]' + 
                 reverse(some_view_url, args(instance_pk) ).read()
    transaction.on_commit(on_commit)

post_save.connect( trigger_on_post_save, A )
Run Code Online (Sandbox Code Playgroud)

这里的想法是,您将在提交事务调用端点,因此事务中涉及的实例将已保存;).


Eva*_*ley 12

我相信post_save会在保存发生后触发,但会在事务提交到数据库之前触发.默认情况下,Django仅在请求完成后提交对数据库的更改.

解决问题的两种可能方案:

  1. 手动管理您的交易,并在提交后触发自定义信号.
  2. 让您的第二个流程等待一段时间以便请求完成.

说实话,你的整个设置似乎有点讨厌.您可能应该查看Celery以进行异步任务排队.

  • 只是想补充一点,使用芹菜不会改变问题 - 我有一个芹菜任务获取旧数据,因为所请求的save()的事务尚未提交.您需要在Task.run()中添加延迟,或者 - 更好 - 实例化任务"post_commit"而不是"post_save".Django还没有提供这个信号,但是看看https://github.com/davehughes/django-transaction-signals (9认同)
  • django的默认行为是在每次保存时提交,而不是在请求完成后提交.此外,post_save信号在提交后发送 (5认同)
  • 顺便说一句,如果你使用芹菜,你需要通过倒计时来调用你的任务,因为如果不这样做,你的对象将无法使用.例如,`tasks.mytask.apply_async(kwargs = {'app_model':app_model,'pk':instance.pk,'field':'photo'},倒计时= 1)`.或者您可以使用,https://github.com/chrisdoble/django-celery-transactions. (2认同)

Mar*_*hyn 7

这是使用装饰器的好地方。yoanis-gil 的答案有一个稍微扩展的版本:

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

def on_transaction_commit(func):
    def inner(*args, **kwargs):
        transaction.on_commit(lambda: func(*args, **kwargs))
    return inner


@receiver(post_save, sender=A)
@on_transaction_commit
def trigger_on_post_save(sender, **kwargs):
    # Do things here
Run Code Online (Sandbox Code Playgroud)