代码之家  ›  专栏  ›  技术社区  ›  Ryan

Jinja2“递归”标记实际上是如何工作的?

  •  8
  • Ryan  · 技术社区  · 17 年前

    我试图在jinja2中编写一个非常简单的树遍历模板,使用一些带有重载特殊方法的自定义对象(getattr、getitem等)。这看起来很简单,树的python遍历也可以很好地工作,但是Jinja的递归工作方式有些我不理解。代码如下所示:

    from jinja2 import Template
    
    class Category(object):
    
        def __init__(self, name):
            self.name = name
            self.items = {}
            self.children = True
    
        def __iter__(self):
            return iter(self.items)
    
        def add(self, key, item):
            self.items[key] = item
            return item
    
        def __getitem__(self, item):
            return self.items[item]
    
        def __getattr__(self, attr):
            try:
                return self.items[attr]
            except KeyError:
                raise AttributeError(attr)
    
        def __str__(self):
            return "<Category '%s'>" % self.name
    
    template = '''
    <saved_data>
    {% for key in category recursive %}
        {% set item = category[key] %}
        {% if item.children %}
            <category name="{{key}}">
                {{ loop(item) }}
            </category>
        {% else %}
            <item name="{{ key }}" value="{{ item }}" />
        {% endif %}
    {% endfor %}
    </saved_data>
    '''
    
    b = Category('root')
    c = b.add("numbers", Category('numbers'))
    c.add("one", 1)
    c.add("two", 2)
    c.add("three", 3)
    d = b.add("letters", Category('letters'))
    d.add('ay','a')
    d.add('bee','b')
    d.add('cee','c')
    e = d.add("bools", Category('bools'))
    e.add('tru', True)
    e.add('fals', False)
    
    def walk(c, depth=0):
        for key in c:
            item = c[key]
            print (' '*depth) + str(item)
            if hasattr(item, 'children'):
                walk(item, depth+3)
    print "Python walking the tree:"
    walk(b)
    
    print ""
    print "Jinja2 Walking the tree:"
    t = Template(template)
    print t.render(category = b)
    

    1 回复  |  直到 17 年前
        1
  •  8
  •   Denis Otkidach    17 年前

    正如我从代码中看到的,您正确地理解了递归,除了一件事:它确实替换for语句中的iterable,但不更新变量( category set 原始文件中的标记查找 ,没有一个传递给 loop() .

    我建议换衣服 __iter__() self.items.iteritems() 和模板,以:

    <saved_data>
    {% for key, item in category recursive %}
            {% if item.children %}
                    <category name="{{key}}">
                            {{ loop(item) }}
                    </category>
            {% else %}
                    <item name="{{ key }}" value="{{ item }}" />
            {% endif %}
    {% endfor %}
    </saved_data>