save() 方法是不是异步提交更改?
Posted
技术标签:
【中文标题】save() 方法是不是异步提交更改?【英文标题】:Is save() method commiting changes asynchronously?save() 方法是否异步提交更改? 【发布时间】:2016-06-16 17:49:29 【问题描述】:我有一个带有 Django Admin (v. 1.9.2) 的简单 class
模型,如下所示:
from django.contrib.auth.models import User
class Foo(models.Model):
...
users = models.ManyToManyField(User)
bar = None
我也像这样重载了save()
方法:
def save(self, *args, **kwargs):
self.bar = 1
async_method.delay(...)
super(Foo, self).save(*args, **kwargs)
这里的async_method
是对将在 Celery 上运行的任务的异步调用,它采用 users
字段并向其添加一些值。
同时,每当将用户添加到ManyToManyField
时,我想根据bar
字段的值执行操作。为此,我定义了一个m2m_changed
信号:
def process_new_users(sender, instance, **kwargs):
if kwargs['action'] == 'post_add':
# Do some stuff
print instance.bar
m2m_changed.connect(process_new_users, sender=Foo.users.through)
这就是问题所在。虽然我在save()
方法内部和调用异步方法之前更改了bar
的值,但是当process_new_users()
方法被触发时,instance.bar
仍然是None
(初始值)。
我不确定这是否是因为 save()
方法异步提交更改,并且当 process_new_users()
被触发时,它尚未提交更改并且正在检索旧值,或者我是否缺少其他内容。
我的假设正确吗?如果是这样,有没有办法强制同步提交save()
中的值,以便我可以然后调用异步方法?
注意:也欢迎任何实现此目的的替代方法。
更新 1:在 @Gert 的回答中,我实现了 transaction.on_change()
触发器,因此每当保存 Foo
实例时,我都可以安全地调用异步函数。为此,我实现了这个:
bar = BooleanField(default=False) # bar has became a BooleanField
def call_async(self):
async_method.delay(...)
def save(self, *args, **kwargs):
self.bar = True
super(Foo, self).save(*args, **kwargs)
transaction.on_commit(lambda: self.call_async())
不幸的是,这并没有改变。我现在得到的是False
,而不是None
,而我应该在m2m_changed
信号中得到True
。
【问题讨论】:
save()
方法不会异步提交更改。您在模型(同步)保存之前调用异步任务,因此它可能会或可能不会在模型实际保存后运行。
@knbk 一旦save()
方法成功保存了模型更改,我可以以任何方式运行异步任务吗? (不确定这里是否有 save()
的 post-save 钩子...)
【参考方案1】:
您想确保您的数据库是最新的。在 Django 1.9 中,有一个新的transaction.on_commit
可以触发 celery 任务。
【讨论】:
谢谢。正如我所看到的,没有定义 who 实际触发了钩子 - 我的意思是,有没有办法明确定义只有在save()
方法已提交其更改时才应触发该方法?
@nKn Django 事务系统会触发它。它知道事务何时实际提交到数据库中。
很好,我会试一试并报告结果。谢谢。
似乎传递给on_commit
的触发方法不接受参数,我也无法设法传递它self
。你有一个例子如何使用on_commit
触发器inside一个类?
@nKn 我相信这是可能的:transaction.on_commit(lambda: async_method.delay(self.id))
以上是关于save() 方法是不是异步提交更改?的主要内容,如果未能解决你的问题,请参考以下文章
提交更改后,是不是有通用方法让 EF 返回新模型数据和关联的模型数据?