使用celery将django模型中的保存方法重写为异步的最佳实践

moh*_*ohd 0 django message-queue django-models celery django-celery

我正在建立一个云系统,我有两个应用程序,包括完整功能的服务器应用程序,以及仅包含输入法的客户端应用程序,所以我在客户分支中安装客户端应用程序作为本地应用程序,

我希望在本地保存模型后覆盖应用程序中的任何模型,我将调用芹菜任务将此模型添加到队列中以确保它将到达,即使互联网已关闭,我将重试直到互联网起步,

现在我希望最佳实践能够以通用的方式对任何模型进行操作

我有两个选择

1-这样的覆盖保存方法

def save(self, *args, **kwargs):
    super(Model, self).save(*args, **kwargs)
    save_task.delay(self)
Run Code Online (Sandbox Code Playgroud)

或使用这样的信号

post_save.connect(save-task.delay, sender=Model)
Run Code Online (Sandbox Code Playgroud)

哪一个是最佳实践,我可以为这个项目的所有模型制作泛型?

yuv*_*uvi 7

.save()只是一堆一个接一个地执行的信号.这是文档中流程的缩短版本:

  1. 发出预保存信号.[...]

  2. 预处理数据.[...]大多数字段不进行预处理[...]仅用于具有特殊行为的字段[...]文档尚未包含具有此"特殊行为"的所有字段的列表.

  3. 准备数据库的数据.要求每个字段以可写入数据库的数据类型提供其当前值.大多数字段不需要数据准备[...]整数和字符串'准备写'作为Python对象[...]复杂的数据类型通常需要一些修改.[...]

  4. 将数据插入数据库.[...]

  5. 发出保存后信号.[...]

在您的情况下,您在该过程的中间没有做任何事情.您只需在保存模型后执行此操作.所以不需要使用信号.

现在您真正要问的是如何确保最终执行任务.好:

  1. 我很确定你可以用芹菜来解决这个问题
  2. 您应该将应用程序连接到单个数据库(如果可以),不要在本地保存,然后更新服务器,这可能会变得丑陋.

但是,如果你真的认为互联网发生故障的可能性很大,或者你确定没有更好的方式来链接你的应用程序,我建议你添加一个新的模型来跟踪已经更新的内容.像这样的东西:

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
Run Code Online (Sandbox Code Playgroud)

请注意我是如何将它链接到某个模型,而是给它提供工具来获取你该死的任何模型.然后,对于要跟踪的每个模型,添加以下内容:

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()
Run Code Online (Sandbox Code Playgroud)

然后调度将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()
Run Code Online (Sandbox Code Playgroud)

PS

还记得我提到的事情会变得难看吗?自从我发布这个以来,我已经看到了.请注意我如何使用pk和modelname来确定是否在两个地方都保存了模型,对吧?但是,pk是(默认情况下在django中)一个自动递增的字段.如果应用程序在两个地方运行,或者即使你在本地运行它并且发生了一次错误,那么pks很快就会失去同步.

假设我保存了一次对象,它在本地和服务器上获得了1的pk.

local             server
name    pk   ++   name    pk
obj1    1    ++   obj1    1
Run Code Online (Sandbox Code Playgroud)

然后我保存另一个,但互联网下降.

local             server
name    pk   ++   name    pk
obj1    1    ++   obj1    1
obj2    2    ++
Run Code Online (Sandbox Code Playgroud)

下次启动时,我会添加一个新对象,但这会在调度任务运行之前发生.所以现在我的本地数据库有3个对象,我的服务器有2个,那些有不同的pk,得到它?

local             server
name    pk   ++   name    pk
obj1    1    ++   obj1    1
obj2    2    ++   obj3    2
obj3    3    ++
Run Code Online (Sandbox Code Playgroud)

在调度任务运行后,我们将拥有:

local             server
name    pk   ++   name    pk
obj1    1    ++   obj1    1
obj2    2    ++   obj3    2
obj3    3    ++   obj2    3
Run Code Online (Sandbox Code Playgroud)

看看这有多么容易失控?要解决此问题,每个跟踪的模型都必须具有某种唯一标识符,您需要以某种方式告诉Track模型如何遵循它.这很头疼.最好不要在本地保存,而是将所有内容链接在一起