【问题标题】:Django model save() method not called when using Model.objects.create()使用 Model.objects.create() 时未调用 Django 模型 save() 方法
【发布时间】:2019-08-18 02:56:16
【问题描述】:

我的模型Image 中有一个主键 ID CharField,我想为新创建的对象创建唯一的 ID。我尝试通过覆盖模型的保存方法来实现这一点:

    def save(self, *args, **kwargs):
        if not self.pk: # is created
            self.id = uuid.uuid4().hex
            while Image.objects.filter(id=self.id).exists():
                self.id = uuid.uuid4().hex
        return super().save(*args,**kwargs)

问题是,save() 在我用Image.objects.create() 创建对象时似乎没有被调用,它只有在我用image=Image(...) 创建对象然后调用image.save() 时才被调用。结果,除非指定,否则新创建的对象没有分配 id,这导致 PostgreSQL 抛出 non_unique primary key 错误。

如何确保在调用 Image.objects.create() 时创建唯一 ID?

Django 版本:1.11.3

更新:我意识到被覆盖的 save() 方法也没有被调用。原来问题是我在迁移中调用模型的save 方法。正如this post 中指出的那样,自定义模型方法在迁移中不可用。我必须将模型的保存方法复制到迁移文件中。

【问题讨论】:

  • UUID(通用唯一标识符)的要点是您始终可以假设两个 UUID 是不同的。您最好将id 字段设置为default=uuid.uuid4uuid4 之后没有括号),这将在对象创建时自动发生。
  • 我希望 :( 数据库是在我之前设计的,现在很难在不丢失数据的情况下更改主键字段,所以这不是我的选择。
  • docs To create an object, instantiate it using keyword arguments to the model class, then call save() to save it to the database. 所以请说明你为什么这么认为?
  • 你是模特pk姓名id吗?
  • 文档还说To create and save an object in a single step, use the create() method. 所以我希望使用create() 会导致问题。此外,代码库很大,并且有不同的方法来创建对象(例如get_or_create())。我不想遍历整个代码库并寻找这些方法来查看它们是使用create() 还是save()

标签: django postgresql


【解决方案1】:

一般情况下无法做到这一点。在您的 if 语句检查 ID 尚不存在和您设置它之间,其他东西可以添加一个具有该 ID 的新行。这就是使用其他解决方案的原因——数据库确保唯一的自动递增 ID,或者唯一机会非常小的 UUID。

幸运的是,您使用了其中一个。使用 UUID 的习惯是假设它们是唯一的。

方法是将返回唯一 ID 的函数设置为字段的默认值:

def uuid_hex():
    return uuid.uuid4().hex

class YourModel(models.Model):
    id = CharField(unique=True, primary_key=True, default=uuid_hex, null=False)

【讨论】:

  • 忘记了默认值必须是实际函数,而不是 lambda。这行得通吗?
  • 我认为确实如此。问题是我试图在迁移中执行此操作(请参阅我的更新)。
  • @yam:啊,是的,在迁移中,您不会得到真正的模型类,而是会得到与迁移时模型具有相同字段的类似模型的东西。
【解决方案2】:

您应该在覆盖模型save 方法时调用真正的save 方法:

def save(self, *args, **kwargs):
    if not self.pk: # is created
        self.id = uuid.uuid4().hex
        while Image.objects.filter(id=self.id).exists():
            self.id = uuid.uuid4().hex
    super(Image, self).save(*args, **kwargs)

检查文档: Overriding predefined model methods

另外,覆盖默认的id 也不是一个好主意。如果您需要另一个唯一字段用作 ID 之类的东西,那么我建议在默认的 id 字段旁边添加另一个字段。

【讨论】:

  • 对不起,我忘记添加那部分了。我确实调用了保存方法。更新我的帖子。
  • 哦,好吧,但我仍然认为这不是一个好主意。在 django 级别分配 id 不是一个好主意。让主 id 由它最擅长的数据库创建并添加另一个字段。
  • 我同意。不幸的是,数据库是由其他人以这种方式设计的,事实证明,在不破坏与 Django 迁移的 M2M 关系的情况下,将 ID 字段更改为自增整数字段非常困难(如果不是不可能的话)。所以这是我能想到的唯一其他解决方案。
  • 我明白了。如果数据量不大,我会尝试将数据转换为更适合 django 和新技术的新结构,但我仍然没有足够的信息来实际推荐它。祝你好运;)
猜你喜欢
  • 2021-02-28
  • 1970-01-01
  • 2015-10-24
  • 2021-04-10
  • 2013-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多