简体   繁体   中英

Generate hierarchical JSON tree structure from Django model

I have a Django model as

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)

to represent biological taxonomy classification as shown here:

在此输入图像描述

I have classification records of over 5,000 species. I need to generate JSON hierarchical structure as shown below.

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

Can you suggest me any method(s) to do so?

You can do the following:

  1. Transform a list of Classifications to a nested dict.
  2. Transform nested dict to the required format

Samples here will operate on slightly reduced Classification class to improve readability:

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

First part:

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 is just a nice tool to guarantee existence of the key in a dictionary, it receives any callable and use it to create a value for missing key.

Now we have nice nested dictionary in the form of

{
    'Kingdom1': { 
        'Phylum1': { 
            'Class1': ["Species1", "Species2"],
            'Class2': ["Species3", "Species4"],
        },
        'Phylum2': { ... }
     },
     'Kingdom2': { 'Phylum3': { ... }, 'Phylum4': {... } }
}

Part two: converting to desired output

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')

I believe it's self-explanatory - we just convert passed dictionary to desired format and recurse to it's content to form children.

Complete example is here .

Two notes:

  1. Written in python 3. Replacing source.items() with source.iteritems() should suffice to run in python 2.
  2. You haven't specify what leafs should looks like, so I just assumed that leaf nodes should be genus with all species attached as children . If you want species to be leaf nodes - it's pretty straightforward to modify the code to do so. If you have any trouble doing so - let me know in comments.

Finally got what I wanted. Code is not beautiful, near ugly, yet somehow I got what I wanted.

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")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM