在django模型自定义save()方法中,您应该如何识别新对象?

Mik*_*keN 152 django django-models

我想在保存新记录(不更新现有记录)时在Django模型对象的save()方法中触发特殊操作.

检查(self.id!= None)是否必要且足以保证自我记录是新的而不是更新?这可能会忽略任何特殊情况?

Dav*_*ith 190

self.pk is None:
Run Code Online (Sandbox Code Playgroud)

在新的Model对象中返回True,除非该对象具有UUIDFieldas primary_key.

您可能需要担心的极端情况是,除id之外的字段是否存在唯一性约束(例如,其他字段上的辅助唯一索引).在这种情况下,您仍然可以拥有新记录,但无法保存.

  • 在使用`None`对象检查身份时,您应该使用`is not`而不是`!=` (19认同)
  • 这不是正确的答案.如果使用`UUIDField`作为主键,`self.pk`永远不会是`None`. (4认同)
  • 并非所有模型都具有id属性,即通过`models.OneToOneField(OtherModel,primary_key = True)`扩展另一个模型.我想你需要使用`self.pk` (3认同)
  • 在某些情况下,这可能不起作用.请检查以下答案:http://stackoverflow.com/a/940928/145349 (3认同)
  • >`“self._state 不是私有实例变量,但以这种方式命名以避免冲突”`请问有来源吗?@戴夫·史密斯 (2认同)

Sai*_*ail 154

检查self.pk我们可以检查self._state模型的替代方法

self._state.adding is True 创建

self._state.adding is False 更新

我从这个页面得到了它

  • 这是使用自定义主键字段时唯一正确的方法. (11认同)
  • 不确定`self._state.adding`如何工作的所有细节,但是公平的警告,如果你检查它_after_调用`super(TheModel,self).save(*args,似乎总是等于`False`**kwargs)`:https://github.com/django/django/blob/stable/1.10.x/django/db/models/base.py#L799 (9认同)
  • @guival:`_state`不是私密的; 就像`_meta`一样,它的前缀是下划线,以避免与字段名称混淆.(注意它是如何在链接文档中使用的.) (7认同)
  • 这是正确的方法,应该投票/设置为正确答案。 (2认同)
  • 不过,在使用 Django 中的私有属性或方法时,您应该始终小心,所以即使它有效,它也可能不是最好的解决方案 (2认同)
  • 这是最好的方法。我用过is_new = self._state.adding,然后是super(MyModel,self).save(* args,** kwargs)然后是if is_new:my_custom_logic() (2认同)
  • 真正的正确答案。有时您可以创建一个设置 ID 的对象,因此检查 pk is None 将不起作用。 (2认同)

小智 44

检查self.id假定这id是模型的主键.更通用的方法是使用pk快捷方式.

is_new = self.pk is None

  • 专业提示:把这个**BEFORE**放在`super(...).save()`. (11认同)

Kay*_*Ess 38

对于检查self.pk == None足够的,以确定该物体将被插入或更新数据库.

Django O/RM有一个特别讨厌的黑客攻击,基本上是检查PK位置是否有东西,如果是,则执行UPDATE,否则执行INSERT(如果PK为None,则优化为INSERT).

它必须这样做的原因是因为您可以在创建对象时设置PK.虽然在主键的序列列的位置不常见,但这不适用于其他类型的主键字段.

如果你真的想知道你必须做O/RM所做的事情并查看数据库.

当然,您的代码中有一个特定的案例,因此很可能会self.pk == None告诉您需要知道的所有内容,但这不是一般解决方案.

  • 在使用“UUIDField”作为主键的情况下尤其如此:该键不是在数据库级别填充的,因此“self.pk”始终为“True”。 (2认同)

JF *_*mon 10

您可以连接到post_save信号,该信号发送"已创建"的kwargs,如果为true,则表示您的对象已插入.

http://docs.djangoproject.com/en/stable/ref/signals/#post-save

  • 如果有很多负载,这可能会导致竞争条件.那是因为post_save信号是在保存时发送的,但是在事务提交之前.这可能会有问题,并且可能使调试变得非常困难. (8认同)

Kwa*_*nor 7

检查self.idforce_insert标志.

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else
Run Code Online (Sandbox Code Playgroud)

这很方便,因为您新创建的对象(self)具有pk


met*_*mit 6

对于即使在您将 aUUIDField作为主键(正如其他人所指出的,None如果您只是覆盖save)也能正常工作的解决方案,您可以插入 Django 的post_save信号。将此添加到您的models.py

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance, e.g.
        # instance.generate_avatar()
        # instance.send_email_notification()
        pass
Run Code Online (Sandbox Code Playgroud)

此回调将阻塞该save方法,因此您可以在响应通过线路发回之前执行诸如触发通知或进一步更新模型之类的操作,无论您使用的是表单还是 Django REST 框架进行 AJAX 调用。当然,负责任地使用并将繁重的任务卸载到作业队列,而不是让您的用户等待:)


Jor*_*dan 5

我这次谈话的时间已经很晚了,但我遇到了一个问题,当self.pk有一个与之关联的默认值时会被填充.

我解决这个问题的方法是在模型中添加一个date_created字段

date_created = models.DateTimeField(auto_now_add=True)

从这里你可以去

created = self.date_created is None