2009-10-13 7 views
8

Próbuję napisać bardzo prosty szablon chodzenia po drzewach w jinja2, używając pewnych niestandardowych obiektów z przeciążonymi specjalnymi metodami (getattr, getitem, itp.) Wydaje się to proste, a równoważny spacer pythonowy z drzewa działa dobrze, ale jest coś w sposobie, w jaki działa rekursja Jinji, której nie rozumiem. Kod jest wyświetlany poniżej:Jak działa tag rekurencyjny Jinja2?

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) 

Szablon zgłasza wyjątek, tak jakby rekursja faktycznie nie miała miejsca. Wywołanie wewnętrzne jest wykonane, ale jakoś odniesienie do "kategorii" nadal odnosi się do rodzica. Co daje tutaj? Musi istnieć coś bardzo podstawowego, czego mi brakuje na temat tego, jak te szablony rekurencyjne mają działać. (Lub coś bardzo głupiego, co robię, czego po prostu nie widzę)

+1

Powinieneś publikować na liście dyskusyjnej pocoo-libs. Armin (twórca Jinja) zobaczy twój post. http://groups.google.com/group/pocoo-libs/topics –

Odpowiedz

7

Jak widzę, z twojego kodu rozumiesz poprawnie rekurencję, z wyjątkiem jednej rzeczy: zastępuje ona iterowalne w instrukcji for, ale nie robi tego ". t zmienna (category w kodzie) zmiana pierwotnie używane w nim. Tak więc, zagnieżdżone iteracje pętli przez dzieci, ale set tag wyszukiwań w oryginalnym category, nie jeden przeszedł do loop().

Proponuję zmianę __iter__() sposób, aby powrócić self.items.iteritems() i szablon do:

<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>