将 Django 模型中的保存方法覆盖为使用 celery 异步的最佳实践
Posted
技术标签:
【中文标题】将 Django 模型中的保存方法覆盖为使用 celery 异步的最佳实践【英文标题】:best practice to override saving method in django model to be async using celery 【发布时间】:2014-03-20 14:06:14 【问题描述】:我正在构建一个云系统,我有两个应用程序, 包含完整功能的服务器应用程序和仅包含输入法的客户端应用程序, 所以我在客户分支中安装客户端应用程序作为本地应用程序,
我想在本地保存模型后覆盖应用程序中的任何模型,我将调用 celery 任务将此模型添加到队列中以确保它会到达,即使互联网关闭,我也会重试直到互联网起床了,
现在我希望最好的做法是对任何模型都有一个通用的方法
我有两个选择
1- 像这样覆盖保存方法
def save(self, *args, **kwargs):
super(Model, self).save(*args, **kwargs)
save_task.delay(self)
或使用这样的信号
post_save.connect(save-task.delay, sender=Model)
哪一个是最佳实践,我可以使它适用于该项目的所有模型?
【问题讨论】:
似乎在您将我的答案标记为正确后的一段时间内,您为此打开了一笔赏金。我很想知道我的答案中遗漏了什么,也许可以尝试自己赢得那笔赏金。 【参考方案1】:.save()
只是一堆一个接一个地执行的信号。这是来自the documentation的流程的简化版本:
发出预保存信号。 [...]
预处理数据。 [...] 大多数字段不进行预处理 [...] 仅用于具有特殊行为的字段 [...] 文档还没有包含所有字段的列表 “特殊行为。”
为数据库准备数据。 要求每个字段以可写入到 数据库。大多数字段不需要数据准备 [...] 整数和字符串是 “准备写”作为 Python 对象 [...] 复杂的数据类型通常 需要一些修改。 [...]
将数据插入数据库。 [...]
发出保存后信号。 [...]
在您的情况下,您在该过程的中间没有做任何事情。您只需要在模型已保存后执行此操作。所以不需要使用信号。
现在您真正要问的是如何确保最终执行任务。嗯:
-
我很确定你可以用 celery 解决这个问题
您应该将应用程序连接到单个数据库(如果可以的话),不要在本地保存内容然后更新服务器,这可能会变得很丑。
但是,如果您确实认为互联网很可能会出现故障或类似情况,并且您确定没有更好的方法来链接您的应用,我建议您添加一个跟踪更新内容的新模型。像这样的:
class Track(models.Model):
modelname = models.CharField(max_length=20)
f_pk = models.IntegerField()
sent = models.BooleanField()
def get_obj(self):
try:
# we want to do modelname.objects.get(pk=self.f_pk), so:
return getattr( getattr(self.modelname, 'objects'), 'get')(pk=self.f_pk)
except:
return False
请注意,我没有将它链接到某个模型,而是为它提供了工具来获取任何你该死的模型。然后,对于您要跟踪的每个模型,添加以下内容:
class myModel(models.Model):
...
def save(self, *args, **kwargs):
super(Model, self).save(*args, **kwargs)
t = Track(modelname=self.__class__.__name__, f_pk=self.pk, sent=False)
t.save()
然后安排一个任务,将 Track
对象与 sent=False
并尝试保存它们:
unsent = Track.objects.filter(sent=False)
for t in unsent:
obj = t.get_obj()
# check if this object exists on the server too
# if so:
t.sent = True
t.save()
附言
还记得我说过事情会变得丑陋吗?自从我发布这篇文章以来已经有一段时间了,我已经知道如何了。请注意我如何使用 pk 和模型名称来确定模型是否保存在两个位置,对吗? 但是,pk 是(默认情况下在 django 中)一个自动递增的字段。如果应用程序在两个地方运行,或者即使您在本地运行它并且发生了一次错误,那么 pks 很快就会不同步。
假设我保存了一次对象,它在本地和服务器上的 pk 均为 1。
local server
name pk ++ name pk
obj1 1 ++ obj1 1
然后我保存了另一个,但互联网中断了。
local server
name pk ++ name pk
obj1 1 ++ obj1 1
obj2 2 ++
下次启动时,我添加了一个新对象,但这发生在计划任务运行之前。所以现在我的本地数据库有 3 个对象,我的服务器有 2 个,它们有不同的 pk,明白了吗?
local server
name pk ++ name pk
obj1 1 ++ obj1 1
obj2 2 ++ obj3 2
obj3 3 ++
在计划的任务运行之后,我们将拥有这个:
local server
name pk ++ name pk
obj1 1 ++ obj1 1
obj2 2 ++ obj3 2
obj3 3 ++ obj2 3
看看这有多容易失控?为了解决这个问题,每个被跟踪的模型必须拥有某种唯一标识符,并且您需要以某种方式告诉Track
模型如何遵循它。真是头疼。最好不要在本地保存东西,而是将所有内容链接在一起
【讨论】:
这正是我要找的,我将创建一个可以接受泛型类型的模型,例如 admin logentry,将记录所有保存的实例,如果未发送此对象的状态,我将发送它然后更新日志扔芹菜,谢谢老兄以上是关于将 Django 模型中的保存方法覆盖为使用 celery 异步的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
使用覆盖保存为抽象模型扩展 Django ModelForm