Обнаружение петель в моделях Django - PullRequest
1 голос
/ 03 августа 2011

У меня есть модель, которая имеет отношения многие ко многим с самим собой.

Я хочу создать проверку для моделей, которые не позволили бы группе быть ее собственной подгруппой или подгруппойэто подгруппы и т. д. Цель состоит в том, чтобы предотвратить ситуацию, которая может привести к циклу / бесконечной рекурсии.

Я пытался реализовать это в методе модели 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')

Заранее благодарим за любую помощь, которую вы можете оказать.

[edit] К вашему сведению, если вы не можете сказать, основываясь на механизме, который я использую для управления транзакциями, я в настоящее время использую django 1.2, потому что это то, что доступно в репозитории EPEL Fedora для RHEL6.Если решение доступно, но требует обновления до 1.3, у меня нет проблем с обновлением.Я также использую Python 2.6.6, потому что это то, что доступно в RHEL6 от RedHat.Я бы предпочел избежать обновления Python, но я очень сомневаюсь, что это актуально.

1 Ответ

0 голосов
/ 03 августа 2011

Должен "цель".действительно быть внутри этого цикла в вашем коде?Похоже, что это заставит его пропустить уровень.

    for sub_group in self.target.all():
        sub_group.target.validate_no_group_loops(seen)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...