Django pre_save信号:检查实例是否已创建而非更新,kwargs['created']是否存在?

39
我正在使用Django的pre_save信号来实现auto_now_add。互联网上有很多关于为什么你应该或不应该自己实现它的讨论,但我不希望对此进行评论。也不希望对是否应该重写保存函数(我有很多使用auto_now_add的模型,因此使用信号是有意义的)进行评论。
我的问题是: 我想检查实例是创建还是更新。根据互联网上的一些来源,可以通过测试 kwargs['created'] 是否为True 来实现此操作。然而,即使实例是新创建的,kwargs中也没有出现'created'。我只是想知道它曾经存在过还是神奇地消失了。
我知道我也可以测试kwargs['instance'].id是否设置(实际上这对我有效),但我想知道kwargs['created']是否仍然存在。

1
需要记住的一件事是,如果设置了实例ID/PK,这并不意味着对象存在于数据库中。一个常见的例子是,如果这些实例是从__fixtures__中加载的。 - Botond Béres
@Botondus:有没有更好的方法(避免你提到的问题),来检查实例是正在被新创建还是在pre_save期间更新? - Heyl1
是的,如果设置了PK,您实际上必须查询数据库以确定是否已经创建了一个实例。 类似这样:MyModel.objects.filter(pk=pk_val).exists() 在Django内部实际上是以类似的方式实现的:http://code.djangoproject.com/browser/django/trunk/django/db/models/base.py#L493 - Botond Béres
谢谢你的提示!我已经把它包含在我的代码中,并会记住这个建议以备将来的项目。 - Heyl1
7个回答

69

主键属性通常在实例第一次保存时由数据库分配。因此,您可以使用类似于if instance.pk is None的代码。


@phasetwenty 你可以锁定表格并额外检查是否存在具有该自然键的记录。或者使用代理键作为主键,自然键作为次要键。 - Gill Bates
在Django中,每个东西都有一个主键,因此这是检查实例是否已保存的非常标准的方法。即使是自然键模型也会有一个pk,您可以检查该pk以确定实例是否已保存。 - Bobort
最好使用公共方法或属性,而不是使用私有方法或属性。 - binarymason

30
根据最新的Django 文档, pre_save 不会发送created参数. 然而, post_save. 我找不到任何关于自1.0版本以来信号发送created的参考资料。

2
非常感谢您的回复。我一定是误读了资料,并假设“created”也出现在pre_save信号中。当你仔细想想,这其实是有道理的。感谢您为我澄清这个问题! - Heyl1
正如Manoj所说,在这种情况下,您可以使用instance.pk并验证该行是否存在,如果存在,则进行更新。 - Darkaico

24

我不确定这是否是推荐的方法,但 @Radagast 的方法对我没用(不确定是否因为我使用自定义 Pk)。

我尝试了以下方法(不确定这是否是最佳方法):

@receiver(pre_save, sender=YourModelName, weak=False, )
def presave_payment_model_check(sender, instance=None, created=False, **kwargs):
    #Reference: https://dev59.com/82gu5IYBdhLWcg3wHjl7
    if instance._state.adding:
        # we would need to create the object
        print "Creating an object"
    else:
        #we are updating the object
        print "Updating an object"
    

参考资料:Django: ModelState的作用是什么?


12

您可以在pre_save中轻松检查实例是创建还是更新,方法如下:

@receiver(pre_save, sender=MyModel)
def pre_save_user(sender, instance, **kwargs): 
    if instance._state.adding:
        print ('Instance created!')
    else:
        print ('Instance updated!')

使用 Django 3.0 进行测试。


4

使用instance._state.adding是最合理的方法,因为您可以确定模型状态是存在还是新建,而不管是否分配了主键。


2

当pk是自定义的uuid字段时,在创建实例时instance.pk也会有值。


这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - コリン

0
你可以试试这个。
@receiver(pre_save, sender=User)
def user(instance, *args, **kwargs):
    if instance.id != None:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接