diff --git a/ietf/group/models.py b/ietf/group/models.py index 93277c13f..c8353ad87 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -94,7 +94,9 @@ class Group(GroupInfo): def is_decendant_of(self, sought_parent): p = self.parent - while ((p != None) and (p != self)): + seen = set() + while ((p != None) and (p != self) and (p not in seen)): + seen.add(p) if p.acronym == sought_parent: return True p = p.parent diff --git a/ietf/group/views_edit.py b/ietf/group/views_edit.py index e4dad3d9b..94d8d45b7 100644 --- a/ietf/group/views_edit.py +++ b/ietf/group/views_edit.py @@ -165,6 +165,20 @@ class GroupForm(forms.Form): MAX_GROUP_DELEGATES, len(self.cleaned_data["delegates"]) - MAX_GROUP_DELEGATES)) return self.cleaned_data["delegates"] + def clean_parent(self): + p = self.cleaned_data["parent"] + seen = set() + if self.group: + seen.add(self.group) + while p != None and p not in seen: + seen.add(p) + p = p.parent + if p is None: + return self.cleaned_data["parent"] + else: + raise forms.ValidationError("A group cannot be its own ancestor. " + "Found that the group '%s' would end up being the ancestor of (%s)" % (p.acronym, ', '.join([g.acronym for g in seen]))) + def clean(self): cleaned_data = super(GroupForm, self).clean() state = cleaned_data.get('state', None)