Jinja2 如何“递归”?标签真的有用吗?

发布于 2024-08-07 11:20:19 字数 1841 浏览 7 评论 0原文

我正在尝试在 jinja2 中编写一个非常简单的树遍历模板,使用一些具有重载特殊方法(getattr、getitem 等)的自定义对象,这看起来很简单,并且等效的 python walk of the tree 工作得很好,但是有一些东西关于 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)

模板引发异常,就好像递归实际上并未发生一样。进行了内部调用,但以某种方式对“类别”的引用仍然引用父级。这里给出了什么?关于这些递归模板应该如何工作,我一定缺少一些非常基本的东西。 (或者是我正在做的一些非常愚蠢的事情,但我只是看不到。

I'm trying to write a very simple, tree-walking template in jinja2, using some custom objects with overloaded special methods (getattr, getitem, etc) It seems straightforward, and the equivalent python walk of the tree works fine, but there's something about the way that Jinja's recursion works that I don't understand. The code is shown below:

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)

The template is raising an exception as if the recursion didn't actually take place. The inner call is made, but somehow the reference to 'category' still refers to the parent. What gives here? There must be something very fundamental I'm missing about how these recursive templates are supposed to work. (Or something very fundamentally silly that I'm doing that I just can't see.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

笑看君怀她人 2024-08-14 11:20:19

正如我从您的代码中看到的,您正确理解了递归,除了一件事:它确实替换了 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>

As I see from your code you understand recursive correctly, except one thing: it does replace iterable in the for statement, but doesn't update variable (category in your code) originally used in it. Thus, you nested loop iterates through children, but set tag lookups in original category, not one passed to the loop().

I suggest changing __iter__() method to return self.items.iteritems() and template to:

<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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文