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)
之后我需要做makemigrations
和migrate
。但是在我的数据库中,我已经有重复名称的记录。
我的问题:我只想对新记录应用约束。有没有办法这样做? (允许旧副本保留在数据库中,但阻止创建新副本)。
目前,我在旧记录中获得了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 仅为新记录添加唯一约束的主要内容,如果未能解决你的问题,请参考以下文章