The following model contains the two almost identical functions list_ancestors and list_descendants. What would be a good way to write this code only once?
class Node(models.Model):
name = models.CharField(max_length=120, blank=True, null=True)
parents = models.ManyToManyField('self', blank=True, symmetrical=False)
def list_parents(self):
return self.parents.all()
def list_children(self):
return Node.objects.filter(parents=self.id)
def list_ancestors(self):
parents = self.list_parents()
ancestors = set(parents)
for p in parents:
ancestors |= set(p.list_ancestors()) # set union
return list(ancestors)
def list_descendants(self):
children = self.list_children()
descendants = set(children)
for c in children:
descendants |= set(c.list_descendants()) # set union
return list(descendants)
def __str__(self):
return self.name
EDIT: The solution derived from the answers below:
def list_withindirect(self, arg):
direct = getattr(self, arg)()
withindirect = set(direct)
for d in direct:
withindirect |= set(d.list_withindirect(arg))
return list(withindirect)
def list_ancestors(self):
return self.list_withindirect('list_parents')
def list_descendants(self):
return self.list_withindirect('list_children')
list_ancestorsuseslist_parents. Descendants are a generalization of children, solist_descendantsuseslist_children.list_ancestorsfirst gets the parents, and then recursively gets the ancestors of the parents.list_descendantsfirst gets the children, and then recursively gets the descendants of the children. As these two similar functions I try to unify are recursive, my "solution"list_withindirectis recursive as well.