As i’m looking for a way to build page by ‘piece’ in Zope Page Templates, macros come to my mind. But macros are usually used with the ‘slot’ behaviour. This is really a nice design, but it’s too hard to maintain for a small app like, i wan to write.
Let’s take a example:
<html metal:use-macro="here/main_template/macros/main"> <body> <span metal:fill-slot="content"> This is the content .. </span> </body> </html>
This will call the main_template page template for the general look and feel, and simply insert the ‘slot’ content in, at the rendering call.
So if you have all pages w/ the same layout this can be really a simple way to do this.
But if you want to have a lot a different layout in the website, this can give you some headhache, because you will need to write a lot of template and fill slot in.
Another way to do this is to call a PT in another one:
<span tal:replace="here/templates/menu"> menu go here</span>
This is really more easily to build pages with different layout, but we need to understand how it works. It will __call__ the rendering on all parts of the template, so you can heat a lot of cpu with that. In a previous review, i discover that Nuxeo CPS use this kind of integration, and the results are really bad: calling 15 times pt.render() in a single page give me 5 seconds to render it.
After a little talk with other project developpers i went to the third way, the un-natural one, but the one that is simple and give me good performance too.
The main idea is to use the macros to build to parts of the page and to include them at the rendering call. (think as a include() in php).
I put all the parts in a file slots_template.pt
<!-- Banner --> <div metal:define-macro="banner" id="banner"> <div class="site_name"> <a href=# tal:attributes="href config/site_url" tal:content="config/site_name">Site Name</a> </div> </div> <!-- side --> ....
And not to include it in a page: i simply call
<div metal:use-macro="here/slots/macros/banner" />
So now i can build page w/ only a couple of macros call, and too much pain. There is a drawback of course, you can’t call in slots methods that the current page don’t know. so writing thrue slot can be included in a page without knowing the page context (like in method 2) but this isn’t a big deal, and you can simply use a aq_parent to do that. ( or play w/ getattr )
Importante note: I use this stuff in Webware, but in Zope you can do the same things.. the major difference is that Zope have aq_parent and can cache (without any code) the pt.render() so second way can be use without too much drawback.