ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

请选择stackoverflow.com/a/35647389/8893667作为正确答案。答案在很多情况下都不起作用,例如 UUIDField pk

E
Emilio Conte

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

self._state.adding is True 创建

self._state.adding is False 更新

我从这个 page


这是使用自定义主键字段时唯一正确的方法。
不确定 self._state.adding 如何工作的所有细节,但公平警告说,如果您在调用 super(TheModel, self).save(*args, **kwargs)之后检查它,它似乎总是等于 Falsegithub.com/django/django/blob/stable/1.10.x/django/db/models/…
但是,在使用 Django 的私有属性或方法时,您应该始终小心,因此即使它有效,它也可能不是最好的解决方案
@guival:_state 不是私有的;与 _meta 类似,它带有下划线前缀以避免与字段名称混淆。 (请注意它在链接文档中的使用方式。)
这是最好的方法。我使用了 is_new = self._state.adding,然后是 super(MyModel, self).save(*args, **kwargs),然后是 if is_new: my_custom_logic()
D
Dave W. Smith

更新: 澄清 self._state 不是私有实例变量,而是以这种方式命名以避免冲突,检查 self._state.adding 现在是更好的检查方式。

self.pk is None:

在新模型对象中返回 True,除非该对象将 UUIDField 作为其 primary_key

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


在使用 None 对象检查身份时,您应该使用 is not 而不是 !=
并非所有模型都具有 id 属性,即通过 models.OneToOneField(OtherModel, primary_key=True) 扩展另一个模型的模型。我认为您需要使用 self.pk
这在某些情况下可能不起作用。请检查此答案:stackoverflow.com/a/940928/145349
这不是正确的答案。如果使用 UUIDField 作为主键,则 self.pk 永远不会是 None
旁注:这个答案早于 UUIDField。
G
Gerry

检查 self.id 假定 id 是模型的主键。更通用的方法是使用 pk shortcut

is_new = self.pk is None


专业提示:将此之前放在super(...).save()
K
KayEss

self.pk == None 的检查不足以确定对象是否要插入或更新到数据库中。

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

之所以必须这样做,是因为您可以在创建对象时设置 PK。虽然在主键有序列列的情况下并不常见,但这不适用于其他类型的主键字段。

如果你真的想知道你必须做 O/RM 所做的事情并在数据库中查找。

当然,您的代码中有一个特定案例,因此 self.pk == None 很可能会告诉您您需要知道的一切,但它不是通用解决方案。


好点子!我可以在我的应用程序中解决这个问题(检查无主键),因为我从未为新对象设置 pk。但这绝对不是对可重用插件或框架的一部分的好检查。
当您自己分配主键并通过数据库分配主键时尤其如此。在这种情况下,最可靠的做法是去数据库旅行。
即使您的应用程序代码没有明确指定 pks,您的测试用例的固定装置也可能。不过,由于它们通常在测试之前加载,因此可能不是问题。
在使用 UUIDField 作为主键的情况下尤其如此:该键不在数据库级别填充,因此 self.pk 始终为 True
此解决方案也适用于这种情况:stackoverflow.com/a/35647389/4379151
T
Tim Tisdall

您可以只连接到发送“已创建”kwargs 的 post_save 信号,如果为真,则您的对象已被插入。

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


如果负载很大,这可能会导致竞争条件。这是因为 post_save 信号是在保存时发送的,但在事务提交之前。这可能是有问题的,并且会使调试变得非常困难。
我不确定事情是否发生了变化(从旧版本开始),但是我的信号处理程序在同一个事务中被调用,所以任何地方的失败都会回滚整个事务。我正在使用 ATOMIC_REQUESTS,所以我不太确定默认值。
K
Kwaw Annor

检查 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

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


J
Jordan

我很晚才进行这次对话,但是当 self.pk 具有与之关联的默认值时,我遇到了填充 self.pk 的问题。

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

date_created = models.DateTimeField(auto_now_add=True)

从这里你可以去

created = self.date_created is None


m
metakermit

对于即使您有一个 UUIDField 作为主键也有效的解决方案(正如其他人所指出的,如果您只是覆盖 save,则它不是 None),您可以插入 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

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


y
yedpodtrzitko

而是使用 pk 而不是 id:

if not self.pk:
  do_something()

v
vikingosegundo

这是这样做的常用方法。

id 将在第一次保存到数据库时给出


S
Swelan Auguste
> def save_model(self, request, obj, form, change):
>         if form.instance._state.adding:
>             form.instance.author = request.user
>             super().save_model(request, obj, form, change)
>         else:
>             obj.updated_by = request.user.username
> 
>             super().save_model(request, obj, form, change)

使用cleaned_data.get(),我能够确定我是否有一个实例,我还有一个CharField,其中为null,而为true,则为空白。这将根据登录的用户在每次更新时更新
S
Sachin

这适用于上述所有情况吗?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...

这将导致额外的数据库命中。
u
user3486626

在 python 3 和 django 3 中,这就是我的项目中的工作:

def save_model(self, request, obj, form, change):
   if not change:
      #put your code here when adding a new object.

A
Alex L

要了解您是更新还是插入对象(数据),请在表单中使用 self.instance.fieldname。在您的表单中定义一个干净的函数并检查当前值条目是否与以前的值相同,如果不是,那么您正在更新它。

self.instanceself.instance.fieldname 与新值进行比较