【问题标题】:How to tell if a model instance is new or not when using UUIDField as a Primary Key使用 UUIDField 作为主键时如何判断模型实例是否是新的
【发布时间】:2016-03-20 21:19:13
【问题描述】:

我有一个模型需要一些后处理(我生成了 body 字段的 MD5)。

models.py

class MyModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    body = models.TextField()
    md5 = models.CharField(max_length=32)
    ...

    def save(self, *args, **kwargs):
        if self.pk is None: # Only for create (edit disabled)
            self.md5 = get_md5(self.body)
            super(MyModel, self).save(*args, **kwargs)

问题是最终块不会执行,因为我看不到检查实例是否是新实例的方法:self.pk 永远不是None,因为在保存之前填充了 UUID。

我想知道处理这个问题的最佳做法是什么。

提前致谢。

更新:

我能想到的唯一解决办法就是直接调用数据库:

  1. 检查id是否存在
  2. 比较 modifiedcreated 字段来判断它是否是编辑

【问题讨论】:

  • 所以,self.md5 是一个新实例的None,对吧?那为什么不用self.md5 代替self.pk 呢?
  • 我可以做到这一点,但还会发生其他一些事情(另一个模型使用 FK 创建回这个模型)。不幸的是,我忽略了将其添加到这个简单的示例中:/
  • 作为可能的解决方案,您可以尝试使用保存后信号。它包含布尔标志 - 已创建。但是这种方法会向 db 添加额外的查询。

标签: python django uuid


【解决方案1】:

编辑

self.pk 永远不会是 None,因为在保存之前会填充 UUID。

不要为id 设置默认值,而是使用一种方法为新实例设置id

class MyModel(...):
    id = models.UUIDField(primary_key=True, default=None,...)

    def set_pk(self):
        self.pk = uuid.uuid4()

    def save(self, *args, **kwargs):
        if self.pk is None:
            self.set_pk()
            self.md5 = get_md5(self.body)
            super(MyModel, self).save(*args, **kwargs)

【讨论】:

  • 我认为您没有理解我的问题,这有什么帮助?
  • 感谢您的帮助,我无法看到(不调用数据库)来判断对象是否是新的。
  • 感谢@xyres。这是一个解决方案,但我将 id = models.UUIDField... 之类的通用功能放在我从所有模型继承的通用模型中,然后我是否必须覆盖所有模型的保存?
  • @DanielvanFlymen 只需将save()set_key() 方法放在父模型中即可。子模型将继承这些方法。
【解决方案2】:

我刚刚在我的项目中遇到了同样的问题,发现你可以检查模型实例的内部状态:

def save(self, *args, **kwargs):
    if self._state.adding: # Only for create (edit disabled)
        self.md5 = get_md5(self.body)
        super(MyModel, self).save(*args, **kwargs)

但是这个解决方案依赖于内部实现,可能会在 Django 更新后停止工作

【讨论】:

    【解决方案3】:

    看起来最简洁的方法是通过从抽象模型继承来确保所有模型都有created 日期,然后您只需检查created 是否具有值:

    models.py

    class BaseModel(models.Model):
        """
        Base model which all other models can inherit from.
        """
        id = fields.CustomUUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        created = models.DateTimeField(auto_now_add=True)
        modified = models.DateTimeField(auto_now=True)
    
        class Meta:
            # Abstract models are not created in the DB
            abstract = True
    
    class MyModel(BaseModel):
        my_field = models.CharField(max_length=50)
    
        def save(self, *args, **kwargs):
            if self.created:
                # Do stuff
                pass
            super(MyModel, self).save(*args, **kwargs)
    

    【讨论】:

      【解决方案4】:

      正如我也回答了here 一样,我发现不需要任何额外的datetime 字段或类似修补的最干净的解决方案是插入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
              self.md5 = get_md5(self.body)
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-11
        • 2014-08-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        相关资源
        最近更新 更多