Jinja2 如何“递归”?标签真的有用吗?
我正在尝试在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如我从您的代码中看到的,您正确理解了递归,除了一件事:它确实替换了 for 语句中的 iterable,但不会更新最初在其中使用的变量(代码中的类别)。因此,您的嵌套循环会遍历子级,但会在原始
category
中查找set
标记,而不是传递给loop()
的标记。我建议更改 __iter__() 方法以返回
self.items.iteritems()
和模板: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, butset
tag lookups in originalcategory
, not one passed to theloop()
.I suggest changing
__iter__()
method to returnself.items.iteritems()
and template to: