使用 UUIDField 作为主键时如何判断模型实例是否是新的

How to tell if a model instance is new or not when using UUIDField as a Primary Key

我有一个模型需要一些 post 处理(我生成 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.pk is never None because a UUID is populated before saving.

不要为 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)

看起来最干净的方法是通过从抽象模型继承来确保所有模型都有 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)

因为我已经回答了 here as well, the cleanest solution I've found that doesn't require any additional datetime fields or similar tinkering is to plug into the Django's 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 个调用的框架。当然,负责任地使用并将繁重的任务卸载到作业队列,而不是让用户等待 :)

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

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更新后失效