Search code examples
pythonjinja2

Flask How to make a tree in jinja2 html?


I have in python a dictionary that represents a tree style parent child relationship. I want to display the dictionary on the webpage. FYI: The dictionary will end up being all names and will very base on the person entering info.

Example dictionary from Python:

dict_ = {'A':['B', 'C'], 'B':['D','E'], 'C':['F', 'G', 'H'], 'E':['I', 'J']}
root = 'A'

The desired HTML output display would be.

A
├── B
│   ├── D
│   └── E
│       ├── I
│       └── J
└── C
    ├── F
    ├── G
    └── H

I not sure how to get this type of display using flask, Jinja, of other options like javascript. Some guidance, partial, or full answers would be great. ( I did learn how to use treelib to display it in terminal but not html.)


Solution

  • In my solution, a helper function is used to create a tree consisting of dicts from the given dict with the lists it contains. Thus, the hierarchy is given to render a structure of nested list elements with a jinja macro.

    def tree_find(e, t):
        if e in t:
            return t
        for v in t.values():
            r = tree_find(e, v)
            if r:
                return r
        return None
    
    @app.route('/')
    def index():
        dict_ = {'A':['B', 'C'], 'B':['D','E'], 'C':['F', 'G', 'H'], 'E':['I', 'J']}
        tree = {}
        for k,v in dict_.items():
            n = tree_find(k, tree)
            (tree if not n else n)[k] = {e:{} for e in v}
        return render_template('index.html', **locals())
    
    {% macro render_tree(d) -%}
        <ul>
        {% for k,v in d.items(): -%}
            <li><span>{{k}}</span>
            {% if v -%}
            {{ render_tree(v) }}
            {% endif -%}
            </li>
        {% endfor -%}
        </ul>
    {%- endmacro %}
    
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Tree</title>
        <style type="text/css">
    
            ul, li {
                position: relative;
            }
    
            ul {
                list-style: none;
                padding-left: 16px;
            }
    
            li::before, li::after {
                content: "";
                position: absolute;
                left: -12px;
            }
    
            li::before {
                border-top: 1px solid #000;
                top: 9px;
                width: 8px;
                height: 0;
            }
    
            li::after {
                border-left: 1px solid #000;
                height: 100%;
                width: 0px;
                top: 2px;
            }
    
            ul > li:last-child::after {
                height: 8px;
            }
    
        </style>
    </head>
    <body>
        {{ render_tree(tree) }}
    </body>
    </html>