Node Tree Navigation

It’s often much more practical to modify child nodes directly instead of giving each its own render_name method. Conveniently you can access child nodes of a node directly as attribute using the child node’s name:

from tdi import html
template = html.from_string("""
<html>
<body>
    <ul class="menu" tdi="menu">
        <li><a href="menu1" tdi="menu1">some menu item</a></li>
        <li><a href="menu2" tdi="menu2">Editing Content &amp; Attributes</a></li>
        <li><a href="menu3" tdi="menu3">Other menu item</a></li>
    </ul>
</body>
</html>
""")

class Model(object):
    def __init__(self, page):
        self._page = page

    def render_menu(self, node):
        if self._page == 1:
            node.menu1.hiddenelement = True
        elif self._page == 2:
            node.menu2.hiddenelement = True
        elif self._page == 3:
            node.menu3.hiddenelement = True

model = Model(page=2)
template.render(model)

However, since the node object defines some names by itself, you cannot address every name by this method [1]. Thatswhy there’s a canonical (but more ugly) method, too, which takes a string as the child node’s name. This method is also reasonable to use if you don’t know the node name in advance (same template as above, just a different model):

class Model(object):
    def __init__(self, page):
        self._page = page

    def render_menu(self, node):
        node("menu%d" % self._page).hiddenelement = True

The output is the same for both cases:

<html>
<body>
    <ul class="menu">
        <li><a href="menu1">some menu item</a></li>
        <li>Editing Content &amp; Attributes</li>
        <li><a href="menu3">Other menu item</a></li>
    </ul>
</body>
</html>

Avoiding Model Calls

Directly accessed child nodes implicitely create another need – to not handle nodes twice (because a render_name method might be called for those nodes as well). You have three options here:

  1. Don’t define render_name methods for those nodes. This may lead to strange effects if later such a method is created nevertheless (really strange if the creator just added a new node with the same name in a completely different context in ignorance of the old usage).

  2. Returning a true value from a render method marks the current node and all of its children as done. No methods are called for this subtree anymore.

    from tdi import html
    template = html.from_string("""
    <html>
    <body>
        <ul class="menu" tdi="menu">
            <li><a href="menu1" tdi="menu1">some menu item</a></li>
            <li><a href="menu2" tdi="menu2">Editing Content &amp; Attributes</a></li>
            <li><a href="menu3" tdi="menu3">Other menu item</a></li>
        </ul>
    </body>
    </html>
    """)
    
    class Model(object):
        def __init__(self, page):
            self._page = page
    
        def render_menu(self, node):
            node("menu%d" % self._page).hiddenelement = True
            return True # avoid calling methods for subnodes
    
        def render_menu1(self, node):
            node.content = "shouldn't come here..."
    
    model = Model(page=2)
    template.render(model)
    
  3. You can flag particular nodes for no automatic method calling in the template already with the * flag.

    <html>
    <body>
        <ul class="menu" tdi="menu">
            <li><a href="menu1" tdi="*menu1">some menu item</a></li>
            <li><a href="menu2" tdi="*menu2">Editing Content &amp; Attributes</a></li>
            <li><a href="menu3" tdi="*menu3">Other menu item</a></li>
        </ul>
    </body>
    </html>
    

The result is the same, of course:

<html>
<body>
    <ul class="menu">
        <li><a href="menu1">some menu item</a></li>
        <li>Editing Content &amp; Attributes</li>
        <li><a href="menu3">Other menu item</a></li>
    </ul>
</body>
</html>

Inspecting Templates

If you’re unsure about the structure of your template (especially after combining different templates or prerendering), you can ask TDI for it.

The → tree attribute of the template object holds the node tree. It can be visualized using the → to_string() method:

from tdi import html
template = html.from_string("""
<html>
<body>
    <h1 tdi="doctitle">doc title goes here</h1>
    <p tdi="intro">Intro goes <span tdi="where">here</span>.</p>
</body>
</html>
""")

print template.tree.to_string()

This script produces a simple nested node name output:

/
  doctitle
  intro
    where
\

This output is useful for debugging purposes. It can also be used for simple automated tests if and how the node structure changed after modifications of a template, as well.

to_string() can also be more verbose:

print template.tree.to_string(verbose=True)

The verbose output gives more node information (if there is some, which is not the case here) and shortened versions of the text between the nodes. That way it’s easily possible to check if the template was parsed correctly (i.e. according to your expectations):

/
  '\n<html>\n<body>\n    '
  doctitle
    'doc title goes here'
  '\n    '
  intro
    'Intro goes '
    where
      'here'
    '.'
  '\n</body>\n</html>\n'
\

For your convenience the the to_string() method is connected to the string representations of the template object (nodes only) and the tree attribute (verbose variant), so you can just write:

print template

in order to get a quick abstract of the template.


[1]The following attributes are owned by the node object: content, copy, ctx, hiddenelement, closedelement, iterate, raw, remove, render, repeat, replace. Python keywords cannot be used either, because attribute access of a python keyword raises a SyntaxError.