Wie funktioniert die Jinja2 „rekursiven“ Tag tatsächlich funktionieren?
Frage
Ich versuche, eine sehr einfache, Tree-Walking-Vorlage in jinja2 zu schreiben, einige benutzerdefinierten Objekten mit überladenen speziellen Methoden (getattr, getitem, usw.) Es scheint einfach, und der äquivalente Python Fuß des Baumes funktioniert gut, aber es gibt etwas über die Art und Weise, dass Jinja Rekursion funktioniert, dass ich nicht verstehe. Der Code ist unten dargestellt:
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)
Die Vorlage wird eine Ausnahme ausgelöst, als ob die Rekursion eigentlich gar nicht stattfinden. Der innere Ruf gemacht, aber irgendwie die Bezugnahme auf ‚Kategorie‘ bezieht sich immer noch auf die Mutter. Was gibt hier? Es muss etwas sehr grundlegender Bedeutung sein Ich vermisse, wie diese rekursive Vorlagen arbeiten sollen. (Oder etwas ganz grundsätzlich dumm, dass ich tue, dass ich einfach nicht sehen.
Lösung
Wie ich aus dem Code sehen Sie richtig verstanden rekursiv, bis auf einer Sache: es iterable in dem for-Anweisung nicht ersetzt, aber keine Variable (category
in Ihrem Code) aktualisieren ursprünglich darin verwendet. So iteriert Sie verschachtelte Schleife durch Kinder, aber set
Tag Lookups in original category
, nicht ein auf die loop()
weitergegeben.
Ich schlage vor Wechsel __iter__()
Methode zur Rückkehr self.items.iteritems()
und Vorlage:
<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>