Django post_另存为芹菜任务奇怪的行为

Django post_另存为芹菜任务奇怪的行为,django,celery,Django,Celery,我有以下代码: @receiver(post_save, sender=SomeModel, dispatch_uid="build") def handle_creation(sender, instance, created, **kwargs): if created == True: build.delay(instance) @task() def build(instance): instance.status = 'Processing'

我有以下代码:

@receiver(post_save, sender=SomeModel, dispatch_uid="build")
def handle_creation(sender, instance, created, **kwargs):
    if created == True:
        build.delay(instance)

@task()
def build(instance):
    instance.status = 'Processing'
    instance.save()

    #some heavy instructions here
    #. . . .
    #. . . .

    instance.status = 'Finished'
    instance.save()
我产生以下错误:

IntegrityError: duplicate key value violates unique constraint DETAIL:  Key (id)=(13) already exists.
但是,如果我删除第一个实例,保存所有工作正常。芹菜处理任务时,sql指令似乎没有完成。如何修复它


谢谢。

您的代码有两个问题

第一个是最大的,您正在将对象实例传递给任务-这在芹菜文档中被明确标记为错误的方法,基本上您正在做的是在某种状态下序列化对象并将其传递给芹菜进行处理;但与此同时,这个目标可能会改变;作为一种解决方案,您应将对象id作为参数传递,以便芹菜任务可以获取新鲜的对象id:

build.delay(instance.pk)

...

@task
def build(my_key):
    instance = SomeModel.objects.get(pk=my_key)
    instance.status = 'Processing'
    instance.save()
第二个问题本质上很微妙,很少出现在雷达上。您的代码的第一部分可以在事务中调用,这意味着可能会出现这样的情况:芹菜中的任务比事务提交快,然后您的模型将首先保存在芹菜任务中,然后按事务保存—这里有一个问题

若您按照上述建议更改代码,那个么第二个问题描述的情况可能不会发生,或者它将显示不同的错误

为了避免此类问题,最好从1.9版Django中引入的transaction.oncommit处理程序调用芹菜任务

还有一条评论,我可以看到您正在更改对象的状态:

instance.status = 'Processing'
很可能是作为一种信息,但也可能用作一种锁定机制。。。QuerySet的更新方法有一个非常好的选项select_,它允许您在事务期间锁定对象。这对于芹菜任务特别好,当您执行以下操作时:

instance = SomeModel.objects.select_for_update().get(pk=my_key)
它将停止您的任务,等待其他任务完成。不要忘记将@transaction.atomic置于此任务之上


如果您将传递nowait=True以选择要更新的\u,它将毫不延迟地生成一个异常,允许您处理这种情况。

您可以将force\u update=True作为参数传递给save方法。哦,存在raise databaseError强制更新不会影响任何行。在force_更新之后