django模型中的循环检测

时间:2011-08-03 00:25:17

标签: python django django-models django-validation

我有一个与自身有很多关系的模型。

我想在模型上创建一个验证,它可以防止一个组成为它自己的子组,或者它的子组的子组,等等。目的是防止一个可能导致循环/无限的情况递归。

我已经尝试在模型clean()方法中实现它,如下所示。

我也尝试使用事务在模型save()方法中实现它。

在这两种情况下,我最终都处于无效更改(错误地)保存到数据库的情况,但如果我尝试对任一实例进行进一步更改,则验证会检测到错误,但此时,坏数据已存在于数据库中。

我想知道这是否可能,如果是这样,如果可以在模型验证中这样做,那么我不必确保我的团队中的每个人都记得从他们创建的所有表单中调用这些验证。将来

没有进一步的延迟,代码:

class Group(models.Model):
    name = models.CharField(max_length=200)
    sub_groups = models.ManyToManyField('self', through='SubGroup', symmetrical=False)

    def validate_no_group_loops(self, seen=None):
        if seen is None:
            seen = []
        if self.id in seen:
            raise ValidationError("LOOP DETECTED")
        seen.append(self.id)
        for sub_group in self.target.all():
            sub_group.target.validate_no_group_loops(seen)

    # I thought I would use the standard validation mechanism in the clean()
    # method, but it appears that when I recurse back to the group I started 
    # with, I do so with a query to the database which retreives the data before
    # it's been modified. I'm still not 100% sure if this is the case, but
    # regardless, it does not work.
    def clean(self):
        self.validate_no_group_loops()

    # Suspecting that the problem with implementing this in clean() was that 
    # I wasn't testing the data with the pending modifications due to the 
    # repeated queries to the database, I thought that I could handle the
    # validation in save(), let the save actually put the bad data into the
    # database, and then roll back the transaction if I detect a problem.
    # This also doesn't work.
    def save(self, *args, **kwargs):
        super(Group, self).save(*args, **kwargs)
        try:
            self.validate_no_group_loops()
        except ValidationError as e:
            transaction.rollback()
            raise e
        else:
            transaction.commit()


class SubGroup(models.Model):
    VERBS = { '+': '+', '-': '-' }
    action = models.CharField(max_length=1, choices=VERBS.items(), default='+')
    source = models.ForeignKey('Group', related_name='target')
    target = models.ForeignKey('Group', related_name='source')

提前感谢您提供的任何帮助。

[编辑]仅供参考,如果您无法根据我用于管理交易的机制来判断,我目前正在使用django 1.2,因为这是RHEL6的fedora EPEL存储库中可用的内容。如果解决方案可用但需要升级到1.3,我升级没问题。我也在使用python 2.6.6,因为这是RedHat在RHEL6中可用的。我宁愿避免python升级,但我非常怀疑它是否相关。

1 个答案:

答案 0 :(得分:0)

应该“瞄准”。你的代码真的在这个循环中吗?这似乎会让它跳过一个级别。

    for sub_group in self.target.all():
        sub_group.target.validate_no_group_loops(seen)