Python 为什么我可以在访问对象期间访问它';s post_save信号,但不是当我在该信号中触发在另一个进程上调用它的代码时
好了,我对django的信号有意见 我有一个模型 为了加快页面加载的响应速度,我正在卸载一些必须完成的密集处理,通过调用我们正在运行的第二个本地主机Web服务器,这两个服务器都使用相同的数据库。我看到调用进程可以检索对象,但被调用进程不能。端口80和端口[port]都指向运行在同一数据库上的django进程。Python 为什么我可以在访问对象期间访问它';s post_save信号,但不是当我在该信号中触发在另一个进程上调用它的代码时,python,django,django-signals,Python,Django,Django Signals,好了,我对django的信号有意见 我有一个模型 为了加快页面加载的响应速度,我正在卸载一些必须完成的密集处理,通过调用我们正在运行的第二个本地主机Web服务器,这两个服务器都使用相同的数据库。我看到调用进程可以检索对象,但被调用进程不能。端口80和端口[port]都指向运行在同一数据库上的django进程。 在models.py中 class A(models.Model): stuff... def trigger_on_post_save( sender, instance,
在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 )
In views.py
def some_view_function( request, a_pk ):
#This line raises an object_not_found exception
A.objects.get( pk=a_pk )
此外,在urlopen调用引发异常后,该对象在数据库中不存在。据我所知,post_save是在保存对象并写入数据库后调用的。这不正确吗?我相信在保存发生之后,但在事务提交到数据库之前,post_save会触发。默认情况下,Django仅在请求完成后向数据库提交更改 您的问题有两种可能的解决方案:
不过老实说,你的整个设置似乎有点糟糕。您可能应该研究异步任务队列。在从django admin创建新模型时也有同样的问题。覆盖手动管理事务的
ModelAdmin.save\u model
方法
def save_model(self, request, obj, form, change):
from django.db import transaction
with transaction.commit_on_success():
super(ModelAdmin, self).save_model(request, obj, form, change)
# write your code here
我们遇到了一个类似的问题,我们最终使用了(注意:这只有在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 )
这里的想法是,您将在事务提交后调用端点,因此事务中涉及的实例将已经保存;) 这是一个使用装饰师的好地方。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
我只想补充一点,使用芹菜并不能改变这个问题——我有一个芹菜任务来获取旧数据,因为请求的save()事务尚未提交。您需要在Task.run()中添加延迟,或者-更好-实例化任务“post_commit”而不是“post_save”。Django还没有提供这个信号,但看看Django的默认行为是在每次保存时提交,而不是在请求完成后提交。此外,post_save信号是在commitBTW之后发送的,如果你使用芹菜,你需要用倒计时来调用你的任务,因为如果你不这样做,你的对象还不可用。例如,
tasks.mytask.apply_async(kwargs={'app_model':app_model'pk':instance.pk'field':'photo'},倒计时=1)
。或者您可以使用.On 1.6,在发送到芹菜之前先手动提交事务。到目前为止效果很好。示例:来自django.db import transaction transaction.commit()tasks.mytask.delay(…)
@gdakram强制提交是一个简单的解决方案,但如果您正在操作的代码实际上包装在一个更大的事务中(例如保存多个对象),则可能会提前提交事务。小心使用。它不应该是:反向(一些视图url,args(instance.pk)).read()?+1:这是Django 1.9的简洁的“含电池”解决方案。请注意,您甚至可以从“pre_save”信号中使用它,因为它只会在事务提交时触发,因此,例如,您可以在它访问数据库之前根据需要更改某些模型属性。如果您需要支持Django 1.6-1.8,请查看1.9中添加的功能的来源,这种方法不适用于ModelSerializer.create from rest framework。@MarkMishyn不知道这一点,因为我不相信我们当时正在使用DRF。我很想知道为什么它不能与DRF一起工作。@Yoanisigil因为rest框架的ModelSerializer用M2M分两步创建实例,所以当调用Model.objects.create
时,即使使用transaction.on\u commit
,M2M字段也是空的。您可以在这里检查commit\u on\u success
已在Django>1.8中删除。请参见此以了解替代方案:此解决方案似乎会导致无限循环。如果我在trigger_on_post_save函数中执行instance.save(),则.save()会重新触发@on_transaction_commit,导致递归错误loop@Dev您不必在信号中调用save(),因为它显然会导致重复调用。