I'm also going to write in QuerySet since this will allow you to chain them.
And I will provide answer for both retrieval of all children and all parents.
class PersonQuerySet(QuerySet):
def descendants(self, person):
q = Q(pk=person.pk)
for child in person.children.all():
q |= Q(pk__in=self.descendants(child))
return self.filter(q)
def ancestors(self, person):
q = Q(pk=person.pk)
if person.parent:
q |= Q(pk__in=self.ancestors(person.parent))
return self.filter(q)
Now we need to set PersonQuerySet
as the manager.
class Person(TimeStampedModel):
name = models.CharField(max_length=32)
parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
people = PersonQuerySet.as_manager()
So here's the final query.
albert_einstein = Person.people.get(name='Albert Einstein')
bernhard_einstein = Person.peole.get(name='Bernhard Caesar Einstein')
einstein_folks = Person.people.descendants(albert_einstein).ancestors(bernhard_einstein)
Note:
The following solutions is as slow as as the rest of the other answers earlier. I have inspected the Database hit everytime it recurse to its child/parent. (If anyone can improve further with some optimization & caching, this would be better, perhaps prefetch relevant data before querying). In the meanwhile, mptt is more practical.