Pre-Rendering

Often some parts of a webpage are more dynamic than others. TDI lets you render the less dynamic parts independently from the hot stuff. Let’s start with an example:

from tdi import html
template = html.from_string("""
<html>
<body>
    <h1 tdi="doctitle">doc title goes here</h1>
    <ul>
        <li tdi="menu"><a tdi="link">some menu item</a></li><li tdi=":-menu">
        </li>
    </ul>
    <p tdi="intro" class="edit-intro">Intro goes here.</p>
    <div class="list" tdi="list">
        ...
    </div>
</body>
</html>
""")

class PreModel(object):
    def __init__(self, menu, page):
        self._menu = menu
        self._page = page

    def render_menu(self, node):
        node.repeat(self.repeat_menu, self._menu)

    def repeat_menu(self, node, (href, menuitem)):
        node.link.content = menuitem
        if (node.ctx[0] + 1 == self._page):
            node.link.hiddenelement = True
        else:
            node.link['href'] = href

class Model(object):
    def render_doctitle(self, node):
        node.content = u"Some title"

menu = [
    (u'/some/', u'Some Menu Item'),
    (u'/other/', u'Editing Content & Attributes'),
    (u'/third.html', u'Another Menu Item'),
]
premodel = PreModel(menu=menu, page=2)
model = Model()
template.render(model, prerender=premodel)

The logic is split into two parts, which are in fact rendered independently. First the prerender model is asked. Every node not handled there will be kept in the template and can be manipulated by the “real” model object in the second pass. The second time the template is rendered the prerender model won’t be asked again. Of course, the whole thing only makes sense if you intend to keep the template object around for the next request.

_images/prerender.png

Pre-rendering Schema

The figure above show the main objects when using the pre-rendering feature. The original template is the starting point, but becomes pretty unimportant after the first pre-rendering pass.

Warning

Do not pre-render request specific (or worse: user specific) data.

By default the pre-rendered template is cached forever. This may be too long for some data. The solution for this problem is to version the prerendered state. TDI checks the prerender model for a method called prerender_version(), which takes the current version (starting with None) and returns a tuple containing (a) if the version is outdated (bool) and (b) the new version object. The version object can be anything. TDI doesn’t even look at it. It’s just stored together with the prerendered state and passed to the prerender_version() method. Here’s a simplified example (just to visualize the method’s signature):

class PreModel(object):
    def __init__(self, version):
        self._version = version

    def prerender_version(self, version):
        return self._version != version, self._version

Implementation Notes

The pre-rendering mechanism exploits the fact that TDI‘s template syntax is so simple and non-intrusive. Instead of generating output, you just generate a new template (which is fed to the parser again and then stored for the “real” rendering later).

The template-generation process works semi-automatically by using a different adapter between the rendering engine and your model. Actually it’s a wrapper around the regular rendering-adapter. The wrapper asks your model for each render_name method and if it doesn’t provide one, it generates its own, which simply restores the tdi and tdi:scope attributes. See the source code of → model_adapters.PreRenderWrapper and → model_adapters.RenderAdapter for details.

If you need a different mechanism, you can supply another adapter via the preadapter parameter of the → Template.render() method.