如何在Django模型中设置两个外键字段仅能且必须填写其中一个?
实现方案
方法一:模型层自定义校验(应用层生效,推荐优先使用)
首先导入Django校验异常类,重写模型的clean方法实现规则校验,同时可以重写save方法自动触发校验,避免每次手动调用校验逻辑:
from django.db import models from django.core.exceptions import ValidationError class OrderLign(models.Model): Order = models.ForeignKey(Order,on_delete=models.CASCADE) # 建议删掉原字段的default=0,外键为null=True时默认值设为0容易触发关联主键不存在的报错 Item = models.ForeignKey(Item, on_delete=models.RESTRICT, blank=True, null=True) Pack = models.ForeignKey(Pack,on_delete=models.RESTRICT, blank=True, null=True) def clean(self): # 直接判断外键ID是否存在,无需额外查询数据库,性能更高 has_item = self.Item_id is not None has_pack = self.Pack_id is not None if has_item and has_pack: raise ValidationError("不能同时赋值Item和Pack字段") if not has_item and not has_pack: raise ValidationError("Item和Pack字段必须赋值其中一个") def save(self, *args, **kwargs): # 保存前自动触发校验,Admin后台默认会自动调用clean,重写save可覆盖手动调用save的场景 self.full_clean() super().save(*args, **kwargs)
方法二:新增数据库层面约束(可选,避免脏数据)
如果需要从数据库底层杜绝不符合规则的数据写入,可在模型的Meta类中新增校验约束(Django 2.2及以上版本支持):
class OrderLign(models.Model): # 上述字段定义不变 ... class Meta: constraints = [ models.CheckConstraint( check=( models.Q(Item_id__isnull=True, Pack_id__isnull=False) | models.Q(Item_id__isnull=False, Pack_id__isnull=True) ), name="orderlign_only_one_of_item_pack" ) ]
内容的提问来源于stack exchange,提问作者Ahima
相关产品推荐
相关产品推荐

