如何在Django多对多(M2M)中间表设置host_id与hostname_id联合唯一键
Django多对多关联创建联合唯一键的实现方案
完全可以实现,Django针对不同使用场景提供了对应的实现方式:
- 默认自动生成多对多中间表的场景
Django默认生成的多对多中间表本身已经内置了两个外键字段的联合唯一约束,不需要额外配置,天然就禁止同一host和hostname重复关联。你当前使用的app_host_names如果是Django自动生成的默认中间表,本身就已经满足host_id和hostname_id组合唯一的要求,无需额外修改代码。 - 自定义中间表的场景(更推荐,可控性更高)
如果你需要显式声明约束、或者已经自定义了中间表,可以通过如下方式修改代码:
- 新增自定义中间表模型,在Meta中添加联合唯一约束,如果你要沿用现有表名
app_host_names,可以指定db_table参数避免修改表结构 - 在多对多字段声明中通过
through参数关联自定义中间表
对应的完整代码示例:
import uuid from django.db import models class Hostname(models.Model): name = models.CharField(max_length=150, unique=True, blank=False, null=False) # 新增自定义中间表 class HostHostnameRel(models.Model): host = models.ForeignKey("Host", on_delete=models.CASCADE) hostname = models.ForeignKey(Hostname, on_delete=models.CASCADE) class Meta: # Django 2.2及以上推荐使用UniqueConstraint写法 constraints = [ models.UniqueConstraint(fields=['host', 'hostname'], name='unique_host_hostname_pairs') ] # 旧版本Django可以用如下写法 # unique_together = ("host", "hostname") # 沿用你现有的中间表名,不需要迁移修改表名 db_table = "app_host_names" class Host(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) created = models.DateTimeField(auto_now_add=True) last_updated = models.DateTimeField(auto_now=True) # 多对多字段指定关联自定义中间表 names = models.ManyToManyField(Hostname, through=HostHostnameRel)
注意:执行迁移前请先检查现有中间表中是否存在重复的
host_id和hostname_id组合,如果存在重复数据会导致迁移失败,需要先清理重复数据后再执行迁移。
约束生效后,重复添加同一关联关系时会抛出IntegrityError异常,可以在业务代码中提前捕获处理。
内容的提问来源于stack exchange,提问作者steelcityamir
相关产品推荐
相关产品推荐

