Search code examples
htmlpython-3.xtornado

Recursively display file tree structure with Tornado template


I want to display the file tree inside a particular folder (RESULTS) on my html page. I am using python Tornado. I used the answer to this question to get me most of the way, and modified to try and work with Tornado.

With the code below, the top directory is displayed as the header, and the folders inside the top directory are displayed, but the template doesn't loop through the items in the subtrees.

Here is the render call:

def get(self):
    logging.info("Loading Results tree...")
    self.render("results.html", tree=make_results_tree('RESULTS'))

Here is the make_results_tree function:

def make_results_tree(path):
    tree = dict(name=path, children=[])
    lst = os.listdir(path)
    if (path == 'RESULTS' and len(lst) == 1):
        tree['children'].append(dict(name="No results recorded for today"))
    else:
        for name in lst:
            fn = os.path.join(path, name)
            if os.path.isdir(fn):
                tree['children'].append(make_results_tree(fn))
            elif (name != '.gitkeep'):
                tree['children'].append(dict(name=name))
    return tree

I have verified that the python code above all works as intended, thus the issue is in the template code below (results.html), likely in the if or loop block:

<div class="center">
  <h2>{{ tree['name'] }}</h2>
  <ul>
    {% for item in tree['children'] %}
      <li>{{ item['name'] }}
      {% if locals().get('item["children"]', False) %}
        <ul>{{ loop(item['children']) }}</ul>
      {% end %}</li>
    {% end %}
  </ul>
</div>

Why does the template code not loop through multiple levels of the tree?


Solution

  • loop() is a feature of Jinja2; there is no equivalent in Tornado templates. Instead of anonymous recursion, split the file into two files so that the inner file can call itself recursively.

    Also, locals().get() only performs simple lookups, it can't resolve complex expressions like eval() can. You don't need locals here, just operate on item directly.

    results.html:

    <div class="center">
      <h2>{{ tree['name'] }}</h2>
      <ul>{% module Template('items.html', children=tree['children']) %}</ul>
    </div>
    

    items.html:

    {% for item in children %}
      <li>{{ item['name'] }}
      {% if "children" in item %}
        <ul>{% module Template('items.html', children=item['children']) %}</ul>
      {% end %}</li>
    {% end %}