Django分类自引用外键如何禁止实例将自身设为父级,现有约束不生效
解决方案
你之前使用的UniqueConstraint作用是保证同一父级下不能有重复slug的分类,本身就不具备「禁止分类自引用为父级」的能力,你可以通过两层验证实现需求:
1. 应用层验证(重写模型clean方法)
Django模型的clean方法会在表单、Admin提交数据时自动触发,用于自定义业务规则校验:
from django.core.exceptions import ValidationError class Category(models.Model): parent = models.ForeignKey('self', related_name="children", on_delete=models.CASCADE, blank=True, null=True) #'self' to indicate a self-reference. title = models.CharField(max_length=100) slug = AutoSlugField(populate_from='title', unique=True, null=False, editable=True) logo = models.ImageField(upload_to="catlogo", blank=True, null=True, help_text='Optional') created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title # 新增clean方法做自定义校验 def clean(self): if self.parent_id == self.id: raise ValidationError("禁止将分类自身设置为父级") # 调用父类clean逻辑保留原有校验 super().clean() class Meta: #enforcing that there can not be two categories under a parent with same slug verbose_name_plural = "categories" constraints = [ models.UniqueConstraint(fields=['slug', 'parent'], name='unique_parent'), # 新增数据库层面校验,看第二步说明 models.CheckConstraint( check=~models.Q(parent_id=models.F('id')), name='category_cannot_be_own_parent' ) ]
2. 数据库层校验(新增CheckConstraint)
如果有批量写入、直接操作数据库等绕过Django应用层校验的场景,可以新增数据库层面的检查约束做兜底,上述代码Meta的constraints里已经加入了对应配置,执行makemigrations和migrate命令后就会在数据库生成对应约束,就算跳过了Django的校验逻辑,数据库也会拦截非法的自引用写入。
注意事项
- 如果你的Django版本低于3.2,不支持
CheckConstraint,可以在生成迁移文件后手动添加数据库原生约束逻辑 - 如果是通过
save()方法直接写入数据(非表单/Admin提交),需要手动调用full_clean()方法触发clean校验,否则应用层验证不会生效
内容的提问来源于stack exchange,提问作者Aneeq Ak
相关产品推荐
相关产品推荐

