8 votes

Comment fonctionne réellement la balise "récursive" de Jinja2 ?

J'essaie d'écrire un modèle d'arbre très simple dans Jinja2, en utilisant quelques objets personnalisés avec des méthodes spéciales surchargées (getattr, getitem, etc.) Cela semble simple, et la marche équivalente de l'arbre en Python fonctionne bien, mais il y a quelque chose sur la façon dont la récursion de Jinja fonctionne que je ne comprends pas. Le code est présenté ci-dessous :

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)

Le modèle lève une exception comme si la récursion n'avait pas eu lieu. L'appel interne est effectué, mais d'une manière ou d'une autre, la référence à 'category' fait toujours référence au parent. Que se passe-t-il ici ? Il doit y avoir quelque chose de très fondamental qui m'échappe sur la façon dont ces modèles récursifs sont censés fonctionner. (Ou quelque chose de fondamentalement stupide que je fais et que je ne vois pas.

8voto

Denis Otkidach Points 13111

Comme je le vois dans votre code, vous comprenez correctement la récursivité, à l'exception d'une chose : elle remplace bien iterable dans l'instruction for, mais ne met pas à jour la variable ( category dans votre code) utilisé à l'origine dans celui-ci. Ainsi, votre boucle imbriquée itère à travers les enfants, mais set recherche de balises dans l'original category pas un seul n'est passé à la loop() .

Je suggère de changer __iter__() pour retourner self.items.iteritems() et le modèle à :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X