Django - 覆盖模型保存()

Posted

技术标签:

【中文标题】Django - 覆盖模型保存()【英文标题】:Django - Override model save() 【发布时间】:2018-10-07 14:50:04 【问题描述】:

在我们的项目中,我们使用软删除(模型上的 is_deleted 属性)。 在创建新模型之前,我想首先检查它是否已经存在并被删除,如果是 - 设置 is_deleted=False 并保存已经存在的模型。 所以我决定重写模型的“保存”方法。 (不是 View Create,因为它不仅适用于请求) 问题是所有操作(创建、更新、删除)都调用了“save()”。 有没有办法只在创建时调用被覆盖的方法?

class Model(BaseModel):
    title = models.CharField(max_length=200)
    identifier = models.CharField(max_length=15)

    def save(self, *args, **kwargs):
        try:                                               
            existing_model = Model.objects.active_and_deleted().get(                                                        identifier=self.identifier)

            if self.is_deleted is False:
                existing_model.is_deleted = False
                existing_model.title = self.title
                existing_model.created_at = self.created_at
                existing_model.updated_at = self.updated_at
                existing_model.created_by = self.created_by
                existing_model.deleted_at = None
                super(Model, existing_model).save(args, kwargs)
        except Model.DoesNotExist:
            # in case the Nodel wasn't already exist - continue
            pass
        super(Model, self).save(*args, **kwargs)

【问题讨论】:

您想在重新插入之前“删除”记录吗?为什么? 所以实际上你在你的数据库中没有删除任何东西!您处理所有具有is_deleted=False 的对象。当有人删除某些内容时,您只需将 is_deleted 设置为 True。对吗? @Lemayzeur - 是的。 在创建新模型之前,我想先检查它是否已经存在。为此,您需要一个字段作为比较来测试对象是否已经存在,例如:您可以检查title 【参考方案1】:

您的问题的答案,“有没有一种方法可以只在创建时调用被覆盖的方法?”这是:

    def save(self, *args, **kwargs):

        if not self.id:
            # Object is a new instance

        return super(Model, self).save(*args, **kwargs)

但这里还有其他问题。例如,您的模型不应命名为“模型”。

【讨论】:

谢谢,但第一次进入保存功能时它确实有 id。我正在使用 Model.objects.create(...) 来测试它。 我认为你错了。如果该项目尚未创建,那么它将没有idpk。这是 ORM 工作方式的核心组件。 当“对象有一个 UUIDField 作为它的主键”时,该答案适用。您似乎没有这样做,或者手动设置pk。你是吗? 代替“if not self.id”,尝试“if self._state.adding”。 @PaulTuckett - 使用 self._state.adding 的另一个原因是如果您使用模型字段关键字“primary_key”。在这种情况下,self 没有“id”属性,并且 self.pk 甚至在模型保存之前就被分配了一个值。【参考方案2】:

检查 self.pk 是否为 None 是否可以做你想做的事,这里是一个例子:

class ModelName(mdoels.Model):
    title = models.CharField(max_length=200)
    identifier = models.CharField(max_length=15)

    def save(self, *args, **kwargs):
        if self.pk is None:  # Adding of a new instance
            # let's say we compare instances by `title`
            # you can use other fields for the comparison
            possible_old_instance = Model.objects.filter(title__iexact=self.title)                                            
            if possible_old_instance.exists():
                existing_model = possible_old_instance.first()
                existing_model.is_deleted = False
                existing_model.title = self.title
                existing_model.created_at = self.created_at
                existing_model.updated_at = self.updated_at
                existing_model.created_by = self.created_by
                existing_model.deleted_at = None
                existing_model.save()
                # Nothing happens, we don't call save() method
            else:
                # in case the Model doesn't exist
                super(ModelName, self).save(*args, **kwargs)
        else:
            super(ModelName, self).save(*args, **kwargs)

【讨论】:

我的项目 self.pk 也已经设置为一个新实例。但我会使用自我的不同属性来识别不同的行为。谢谢 您可以这样做,使用 self.pkself.id... 这些属性仅在调用 save() 时返回非空 None【参考方案3】:

如果您使用 UUID 字段而不是默认 ID 字段,检查 self.pk 字段是一个糟糕的主意,因为在我们转到 save 方法之前将 UUID 指定为默认值。嗯,怎么解决呢?最简单最快的解决方案是 self._state.adding 而不是 self.pk

我希望你注意到“状态”这个词之前有一个下划线,但别担心,这个方法是完全合法的,我们这里不叫私有变量。

例子:

def save(self, *args, **kwargs):
    if self._state.adding: # will return true if obcject is newly created
            # Object is a new instance

        return super(Model, self).save(*args, **kwargs)

【讨论】:

以上是关于Django - 覆盖模型保存()的主要内容,如果未能解决你的问题,请参考以下文章

如何以 django 模型形式覆盖保存方法

Django:覆盖保存(模型或管理部分)

保存自定义用户模型时 Django ManyToMany 覆盖

使用覆盖保存为抽象模型扩展 Django ModelForm

将 Django 模型中的保存方法覆盖为使用 celery 异步的最佳实践

django 内联覆盖保存