限制Django模型中的递归外键深度

时间:2013-08-07 04:00:12

标签: django django-models

我有一个Django模型,其中可自由的ForeignKey为'self'。我有兴趣在递归深度上引入硬限制(比如10)。哪里是检查这个的适当位置,我应该扔哪种异常?

我的意思的伪示例:

def limit_recursion_depth(self):
    depth = 0
    model = self
    while model.parent is not None:
        depth += 1
        model = model.parent
    if depth > 10:
        pass # Complain here and stop processing
    else:
        pass # Return the model, save, whatever is normal

我更感兴趣的是像重写模型的save()方法这样的解决方案,而不是任何只适用于管理员的验证。 (例如,我希望能够验证Model.objects.create()。)

更新

即使这个问题主要是学术性的,并且与我不再追求的项目相关,但我想更新此问题,万一有人通过Google发现这个问题。以下内容来自django-mptt documentation

  

要获取所有这些页面标题,您可以执行以下操作:

titles = []
while page:
    titles.append(page.title)
    page = page.parent
     

这是每个页面的一个数据库查询 ...

即使对于小型项目,Django-mptt也更有效率,即使在这种情况下我也应该使用它。

3 个答案:

答案 0 :(得分:4)

也许是这样的?

def save(self, *args, **kwargs):
    if Foo.objects.filter(foo=self.foo).count() > 10:
        raise Exception("not more than 10")
    else:
        super(Foo, self).save(*args, **kwargs)

<强>更新

对于使用self的{​​{1}}引用字段,您可以执行以下操作:

django-mptt

答案 1 :(得分:0)

没有周期的版本:

    def clean_parent(self):
        parent = self.cleaned_data['parent']
        if parent and parent.level >= 10:
            raise ValidationError('Recursion Depth Exceeded')
        return parent

注意:等级从0开始。

答案 2 :(得分:-1)

不幸的是,正如在this answer中所解释的那样,并非是在Django中执行此操作的好方法。例如,每当最终用户点击更改表单上的保存按钮时,上面的方法只会使管理员崩溃500 - 几乎没有帮助。唯一正确的方法是define clean_{fieldname} and throw a ValidationError。例如,它不会验证model.save()中的manage.py shell,但也不会覆盖该验证,例如,queryset.update()

因此,我现在使用的解决方案如下:

def clean_parent(self):
    depth = 0
    model = self.instance
    while model.parent is not None:
        depth += 1
        model = model.parent
    if depth > 10:
        raise ValidationError('Recursion Depth Exceeded')

请注意,这属于模型的管理表单。