Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate nested dict in mako template

I have a the following dict:

{"name1":{"text":"my text 1", "status":"my status"}, "name2":{"text":"my text 2", "status":"my status"}}

Using Mako template, I want to create a html table. I tried the following:

Python snippet:

test = {"name1":{"text":"my text 1", "status":"my status"}, "name2":{"text":"my text 2", "status":"my status"}}
mytemplate = Template(filename='template/index.mako')
return mytemplate.render(data=test)

Mako snippet:

% for key, val in data.iteritems():
    <tr>
        <td>${loop.index + 1}</td>
        <td>${key}</td>
        % for tkey, tval in val.iteritems():
            <td>${tkey}</td>
            <td>${tval}</td>
        % endfor
    </tr>
% endfor

The above snippets raise error('str' object has no attribute 'iteritems').

What is the proper way to iterate over nested dicts?

Thank you

like image 770
florin Avatar asked Sep 07 '25 08:09

florin


1 Answers

It is oddly difficult to do this. I recently was trying to do something very similar, found this post, and was sad to see that it did not have any answers.

You asked for the "proper" way, and I would think that iterating over the items would be the most Pythonic way; but I was also unable to get that to work. I was able to get a recursive parser to work (with the added bonus that this will handle n-levels of nesting).

I got this to work:

from mako.template import Template

template = '''
${handleLevel(pages)}
<%def name="handleLevel(level, depth=0)">
    % if isinstance(level, dict):
            % for item in level:
                % if isinstance(level[item], dict):
                    <!-- print this item for this level -->
                        ${handleLevel(level[item], depth+1)}
                    <!-- end this level (close div or anything that should contain next level -->
                % else:
                    <!-- handle bottom level (linke, or div, etc.) -->
                    ${item}
                    <!-- close any elements for bottom level not already closed -->
                % endif
            % endfor
    % endif
</%def>
'''

nested = {
    'A': {'B': 'C'},
    'D': 'E',
}
page = Template(template).render(pages=nested)
print(page)

If you know you will only ever have one level of nesting, you could probably just do a for loop like:

from __future__ import print_function
from mako.template import Template

template = '''
% for key in data:
    <tr>
        <td>${loop.index + 1}</td>
        <td>${key}</td>
        % for secondKey in data[key]:
            <td>${secondKey}</td>
            <td>${data[key][secondKey]}</td>
        % endfor
    </tr>
% endfor
'''

test = {"name1":{"text":"my text 1", "status":"my status"}, "name2":{"text":"my text 2", "status":"my status"}}
page = Template(template).render(data=test)
print(page)

(tested in Python3 and 2)

Although, now that I've played some more with this, your code works fine for me in Python 2.7.15 and mako version 1.0.7 . . .

like image 142
mshafer Avatar answered Sep 09 '25 22:09

mshafer