当我保存新记录(不更新现有记录)时,我想在 Django 模型对象的 save() 方法中触发一个特殊操作。
检查 (self.id != None) 是否有必要且足以保证自我记录是新的并且没有被更新?这可能会忽略任何特殊情况?
UUIDField pk
检查 self.pk
的替代方法我们可以检查模型的 self._state
self._state.adding is True
创建
self._state.adding is False
更新
我从这个 page
更新: 澄清 self._state
不是私有实例变量,而是以这种方式命名以避免冲突,检查 self._state.adding
现在是更好的检查方式。
self.pk is None:
在新模型对象中返回 True,除非该对象将 UUIDField
作为其 primary_key
。
您可能需要担心的极端情况是除了 id 之外的字段是否存在唯一性约束(例如,其他字段上的二级唯一索引)。在这种情况下,您手头可能仍有新记录,但无法保存。
None
对象检查身份时,您应该使用 is not
而不是 !=
models.OneToOneField(OtherModel, primary_key=True)
扩展另一个模型的模型。我认为您需要使用 self.pk
UUIDField
作为主键,则 self.pk
永远不会是 None
。
检查 self.id
假定 id
是模型的主键。更通用的方法是使用 pk shortcut。
is_new = self.pk is None
super(...).save()
。
对 self.pk == None
的检查不足以确定对象是否要插入或更新到数据库中。
Django O/RM 有一个特别讨厌的 hack,它基本上是检查 PK 位置是否有东西,如果有,则执行 UPDATE,否则执行 INSERT(如果 PK 为 None,则会优化为 INSERT)。
之所以必须这样做,是因为您可以在创建对象时设置 PK。虽然在主键有序列列的情况下并不常见,但这不适用于其他类型的主键字段。
如果你真的想知道你必须做 O/RM 所做的事情并在数据库中查找。
当然,您的代码中有一个特定案例,因此 self.pk == None
很可能会告诉您您需要知道的一切,但它不是通用解决方案。
UUIDField
作为主键的情况下尤其如此:该键不在数据库级别填充,因此 self.pk
始终为 True
。
您可以只连接到发送“已创建”kwargs 的 post_save 信号,如果为真,则您的对象已被插入。
http://docs.djangoproject.com/en/stable/ref/signals/#post-save
ATOMIC_REQUESTS
,所以我不太确定默认值。
检查 self.id
和 force_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
值
我很晚才进行这次对话,但是当 self.pk 具有与之关联的默认值时,我遇到了填充 self.pk 的问题。
我解决这个问题的方法是在模型中添加一个 date_created 字段
date_created = models.DateTimeField(auto_now_add=True)
从这里你可以去
created = self.date_created is None
对于即使您有一个 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 调用,您都可以在通过网络发送回响应之前执行触发通知或进一步更新模型等操作。当然,负责任地使用并将繁重的任务卸载到作业队列,而不是让您的用户等待:)
而是使用 pk 而不是 id:
if not self.pk:
do_something()
这是这样做的常用方法。
id 将在第一次保存到数据库时给出
> 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)
这适用于上述所有情况吗?
if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...
在 python 3 和 django 3 中,这就是我的项目中的工作:
def save_model(self, request, obj, form, change):
if not change:
#put your code here when adding a new object.
要了解您是更新还是插入对象(数据),请在表单中使用 self.instance.fieldname
。在您的表单中定义一个干净的函数并检查当前值条目是否与以前的值相同,如果不是,那么您正在更新它。
self.instance
和 self.instance.fieldname
与新值进行比较
不定期副业成功案例分享
self._state.adding
如何工作的所有细节,但公平警告说,如果您在调用super(TheModel, self).save(*args, **kwargs)
之后检查它,它似乎总是等于False
:github.com/django/django/blob/stable/1.10.x/django/db/models/…_state
不是私有的;与_meta
类似,它带有下划线前缀以避免与字段名称混淆。 (请注意它在链接文档中的使用方式。)is_new = self._state.adding
,然后是super(MyModel, self).save(*args, **kwargs)
,然后是if is_new: my_custom_logic()