在Python/Django中递归收集子级

在Python/Django中递归收集子级,python,django,recursion,Python,Django,Recursion,我有一个这样的模特 class Person(models.Model): name = models.CharField(max_length=55,null=False, blank=False) parent = models.ForeignKey('Person.Person', null=False, blank=False) 我想创建一个递归函数,它最终将返回一个完整的Person family tree的字典 比如说 first_person = Person.o

我有一个这样的模特

class Person(models.Model):
    name = models.CharField(max_length=55,null=False, blank=False)
    parent = models.ForeignKey('Person.Person', null=False, blank=False)
我想创建一个递归函数,它最终将返回一个完整的Person family tree的字典

比如说

first_person = Person.objects.filter(name='FirstPerson')
family_tree = GetChildren(first_person)
其中GetChildren是我的递归函数,它将不断调用GetChildren,直到不再有子函数。。。然后它应该返回一本包含所有这些孩子的字典,就像这样

{
    'name': 'FirstPerson',
    'children': [
        {
            'name': 'FirstPersonChild1'
            'children': [ ... ]
        },
        {
            'name': 'FirstPersonChild2'
            'children': [ ... ]
        }
    ]
}

我从来都不擅长递归,有人能解释一下我将如何实现这一点吗…

您可以通过在模型上编写自定义方法来实现这一点。您称之为的方式如下所示:

first_person = Person.objects.filter(name='FirstPerson')
family_tree = first_person.getChildren()
def getChildren(self):
    children = Person.objects.filter(parent=self)
    # return children
    # OR just format the data so it's returned in the format you like
    # or you can return them as Person objects and have another method
    # to transform each object in the format you like (e.g Person.asJSON())
其中getChildren看起来像这样:

first_person = Person.objects.filter(name='FirstPerson')
family_tree = first_person.getChildren()
def getChildren(self):
    children = Person.objects.filter(parent=self)
    # return children
    # OR just format the data so it's returned in the format you like
    # or you can return them as Person objects and have another method
    # to transform each object in the format you like (e.g Person.asJSON())

这一实施应该有效

def get_family_tree(person):
    """ return a family tree for a Person object """

    children = person.children.all()

    if not children:
        # this person has no children, recursion ends here
        return {'name': person.name, 'children': []}

    # this person has children, get every child's family tree
    return {
        'name': person.name,
        'children': [get_family_tree(child) for child in children],
    }
请注意,这将需要尽可能多的数据库调用。如果遇到性能问题,可以尝试将所有数据提取到内存中

思考递归

考虑递归的一种方法是从基本情况开始,即递归将在哪里结束。在您的案例中,我们知道如果一个人没有孩子,家谱是什么样子的:

{
    'name': 'FirstPerson',
    'children': [],
}
在得到基本情况后,考虑一下必须执行一次递归的问题

在你的情况下,应该是有孩子的父母,但没有孙子。我们知道每个孩子的家谱应该是什么样子——这只是基本情况!这将引导我们找到一个解决方案,在这个解决方案中,我们返回父母的名字,以及每个孩子的家谱列表。导致类似于:

{
    'name': FirstPerson,
    'children': [<each element is a child's family tree>]
}

请参见

您应该尝试django mptt软件包,因为该软件包非常有效,或者出于以下目的:

您可以使用
treeforeingkey()
作为外键

class Person(models.Model):
    ....
    parent = models.ForeignKey('self', related_name='children', blank=True, null=True)

p = Person()
p.children.all() # automatically fetch all Person objects where parent=p
然后,您可以将此方法添加到模型中以获取对象(或者查看我提供的文档以获取子对象,而不是父对象/祖先对象):

Github:

文件:


@删掉你的名字真的很适合你。这只会产生一个层次的结果。我想让树尽可能地往下爬(直到树上的人没有孩子为止)。第一人称可能有一个孩子,但一个孩子可能有4个孩子,每个孩子都有自己的孩子。这是一个很好的思考方式(基本案例等等)。我会试试看,让你知道结果如何。我只需要稍微考虑一下,因为这个模型不存储他们的孩子。。。它存储自己的父对象。您可以直接从个人模型访问子对象,Django会自动为您生成反向关系。如果没有为父字段指定
相关名称
,则自动生成的名称将是
个人集
。您将在
person.person\u set.all()访问子项