用Python / Django递归收集孩子

时间:2016-02-08 23:30:10

标签: 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)

我想创建一个递归函数,最终将返回整个人家谱的字典....

所以例如......

first_person = Person.objects.filter(name='FirstPerson')
family_tree = GetChildren(first_person)

GetChildren是我的递归函数,它将不断调用GetChildren,直到没有更多的孩子......它应该返回一个包含所有这些孩子的字典,如此...

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

我从来没有善于递归,有人会介意如何解决这个问题......

3 个答案:

答案 0 :(得分:2)

此实施应该有效

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自动为ForeignKey生成反向关系。

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

请参阅https://docs.djangoproject.com/en/1.9/ref/models/fields/#foreignkey

答案 1 :(得分:1)

您可以通过在模型上编写自定义方法来实现此目的。你打电话的方式看起来像这样:

import networkx as nx
import numpy as np

pos = np.random.rand(10, 2) #coordinates, (x, y) for 10 nodes
connect = [tuple(np.random.random_integers(0, 9, size=(2))) for x in range(8)] #random connections

#creation of the graph
graph = nx.Graph()
#adding nodes/connections in the graph
for node in range(len(pos)):
    graph.add_node(node)
graph.add_edges_from(connect)

#plot of the nodes using the (x,y) pairs as coordinates
nx.draw(graph, [(x,y) for x,y in pos], node_size=50)

getChildren看起来像这样:

first_person = Person.objects.filter(name='FirstPerson')
family_tree = first_person.getChildren()

答案 2 :(得分:0)

您应该尝试使用django-mptt包,因为它可以很好地用于此目的:

您可以使用TreeForeignKey()作为ForeignKey。

然后,您可以将此方法添加到模型中以获取对象(或查看我提供的文档以获取子项而不是父项/祖先):

def get_category_and_parents(self):
    """ Recursively retrieves parent categories (including self) using MPTT """
    full_category = self.category.get_ancestors(include_self=True)
    return full_category

Github上:

  

https://github.com/django-mptt/django-mptt

文档:

  

http://django-mptt.github.io/django-mptt/mptt.models.html#mptt.models.MPTTModel.get_children