Django 仅为新记录添加唯一约束

Posted

技术标签:

【中文标题】Django 仅为新记录添加唯一约束【英文标题】:Django add unique constraint only for new records 【发布时间】:2019-12-24 19:35:09 【问题描述】:

我有一个 django 模型:

from django.db import models

class TestModel(models.Model):
  name = models.CharField("Name", null=False, blank=False, max_length=300)

一段时间后,我接到了添加唯一约束的任务,使我的新模型如下所示:

from django.db import models

class Test(models.Model):
  name = models.CharField("Title", null=False, blank=False, max_length=300, unique=True)

之后我需要做makemigrationsmigrate。但是在我的数据库中,我已经有重复名称的记录。

我的问题:我只想对新记录应用约束。有没有办法这样做? (允许旧副本保留在数据库中,但阻止创建新副本)。

目前,我在旧记录中获得了IntegrityError,而migrate

【问题讨论】:

【参考方案1】:

不,没有办法做到这一点,因为需要为整个表实现数据库级约束。

您可以:

首先迁移您的旧数据以使您的字段独一无二(例如,在重复名称的末尾附加随机字符串/数字) 或者不在数据库级别添加约束,而是向您的字段添加自定义验证器,该验证器仅在验证时由 Django 进行检查。 或者通过在节省时间进行检查(例如,通过覆盖模型的 save() 方法)。

这最后两种方法的缺点是您不能保证不会添加新的重复记录,例如通过批量添加或将来有人添加代码忘记验证。

请注意,这也意味着现有记录在尝试更改它们时将无法验证。

最后一个替代方法是使用blank=False, null=True, default=None, unique=True 创建一个新字段,您为现有行创建None。如果是 None,则显示现有的 name 字段(旧版),否则显示新的名称字段。

【讨论】:

【参考方案2】:

一种方法是使用部分索引,新版本的 Django 原生支持。这个想法是有一个布尔标志字段duplicate,它标记允许重复的现有记录。该标志可以通过迁移填充:

class TestModel(models.Model):
    name = models.CharField("Name", null=False, blank=False, max_length=300)
    duplicate = models.BooleanField(default=False)

    constraints = [
      models.UniqueConstraint(fields=['name'],
                              condition=models.Q(duplicate=False)),
    ]

【讨论】:

以上是关于Django 仅为新记录添加唯一约束的主要内容,如果未能解决你的问题,请参考以下文章

Django Models如何添加唯一约束

Django 更新序列化程序和唯一的约束

IntegrityError:重复键值违反唯一约束

我可以向 MySQL 中的列组合添加唯一约束吗? [复制]

Django:删除唯一约束并创建迁移

如何在 django 的核心模型中添加“unique_together”约束?