从Django模型生成分层JSON树结构

时间:2014-07-02 04:27:14

标签: python json django

我有一个Django模型

class Classification(models.Model):
    kingdom = models.CharField(db_column='Kingdom', max_length=50)
    phylum = models.CharField(db_column='Phylum', max_length=50)
    class_field = models.CharField(db_column='Class', max_length=50)
    order = models.CharField(db_column='Order', max_length=50)
    family = models.CharField(db_column='Family', max_length=50)
    genus = models.CharField(db_column='Genus', max_length=50)
    species = models.CharField(db_column='Species', max_length=50)

表示生物分类学分类,如下所示:

enter image description here

我有超过5,000种的分类记录。我需要生成JSON层次结构,如下所示。

{
'name': "root",
'children': [
                {
                    'name': "Animalia",
                    'children': [
                        {
                            {
                                'name':"Chordata"
                                'children': [ ... ]
                            }
                        },
                        ...
                        ...
                    ]
                },
                ...
                ...
            ]
}

你能建议我这样做吗?

2 个答案:

答案 0 :(得分:2)

您可以执行以下操作:

  1. Classifications列表转换为嵌套字典。
  2. 将嵌套的dict转换为所需的格式
  3. 此处的示例将在稍微减少的Classification级别上运行,以提高可读性:

    class Classification:
        def __init__(self, kingdom, phylum, klass, species):
            self.kingdom = kingdom
            self.phylum = phylum
            self.klass = klass
            self.species = species
    

    第一部分:

    from collections import defaultdict
    # in order to work with your actual implementation add more levels of nesting 
    # as lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
    nested_dict = defaultdict(
        lambda: defaultdict(
            lambda: defaultdict(list)
        )
    )
    
    for c in all_classifications:
        nested_dict[c.kingdom][c.phylum][c.klass].append(c.species)
    

    defaultdict只是一个很好的工具,可以保证字典中密钥的存在,它接收任何可调用的字符并使用它来创建缺失密钥的值。

    现在我们以

    的形式提供了很好的嵌套字典
    {
        'Kingdom1': { 
            'Phylum1': { 
                'Class1': ["Species1", "Species2"],
                'Class2': ["Species3", "Species4"],
            },
            'Phylum2': { ... }
         },
         'Kingdom2': { 'Phylum3': { ... }, 'Phylum4': {... } }
    }
    

    第二部分:转换为所需的输出

    def nested_to_tree(key, source):
        result = {'name': key, 'children':[]}
        for key, value in source.items():
            if isinstance(value, list):
                result['children'] = value
            else:
                child = nested_to_tree(key, value)
                result['children'].append(child)
    
        return result
    
    tree = nested_to_tree('root', nested_dict')
    

    我相信这是不言自明的 - 我们只是将传递的字典转换为所需的格式并递归到它的内容以形成孩子。

    完整的示例是here

    两个注释:

    1. 用python编写3.用source.items()替换source.iteritems()应该足以在python 2中运行。
    2. 您还没有指定哪些叶子应该是什么样的,所以我只假设叶子节点应该是genus,并且所有species都附加为children。如果您希望species成为叶节点 - 修改代码就可以非常简单。如果您有任何问题 - 请在评论中告诉我。

答案 1 :(得分:1)

终于得到了我想要的东西。代码不漂亮,近乎难看,但不知怎的,我得到了我想要的东西。

def classification_flare_json(request):
    #Extracting from database and sorting the taxonomy from left to right
    clazz = Classification.objects.all().order_by('kingdom','phylum','class_field','genus','species')

    tree = {'name': "root", 'children': []}

    #To receive previous value of given taxa type
    def get_previous(type):
        types = ['kingdom', 'phylum', 'class_field', 'family', 'genus', 'species']
        n = types.index(type)

        sub_tree = tree['children']
        if not sub_tree: return None
        for i in range(n):
            if not sub_tree: return None
            sub_tree = sub_tree[len(sub_tree)-1]['children']

        if not sub_tree: return None
        last_item = sub_tree[len(sub_tree)-1]
        return last_item['name']

    #To add new nodes in the tree
    def append(type, item):
        types = ['kingdom', 'phylum', 'class_field', 'family', 'genus', 'species_id']
        n = types.index(type)

        sub_tree = tree['children']
        for i in range(n+1):
            if not sub_tree: return None
            sub_tree = sub_tree[len(sub_tree)-1]['children']


        sub_tree.append(item)


    for item in clazz:
        while True:
            if item.kingdom == get_previous('kingdom'):
                if item.phylum == get_previous('phylum'):
                    if item.class_field == get_previous('class_field'):
                        if item.family == get_previous('family'):
                            if item.genus == get_previous('genus'):
                                append('genus', {'name':item.species, 'size': 1})
                                break;
                            else:
                                append('family', {'name':item.genus, 'children': []})
                        else:
                            append('class_field', {'name':item.family, 'children':[]})
                    else:
                        append('phylum', {'name': item.class_field, 'children':[]})
                else:
                    append('kingdom', {'name': item.phylum, 'children':[]})
            else:
                tree['children'].append({'name': item.kingdom, 'children':[]})

    return HttpResponse(json.dumps(tree), content_type="application/json")