Out With The Old...
Not too long ago, I introduced you to something called DTML, the Document Template Markup Language. I defined it as HTML on steroids, and spent lots of time and bandwidth showing you how it could be used to build complex Zope applications.
DTML isn't the only thing Zope has going for it, though. Over the next few pages, I'm going to introduce you to a brand-spanking-new creature from the Zope stable. It's called Zope Page Templates, or ZPT, and it's rapidly overtaking DTML as the de-facto standard for developing applications in Zope.
The Zope Web site is pretty wordy when it comes to describing ZPT. It defines ZPT as "a consistent, XML compliant, markup language [which] embed[s] all logic in namespaced attributes ...and provide[s] a means to supply prototypical content to give the presentation designer a sense of context." Or, to put it in simpler terms, ZPT allows Web developers greater flexibility in separating an application's presentation layer from the business logic that drives it, thereby making it possible to easily update one without disrupting the other.
I'm going to show you how in the following pages. Before I begin, though, make sure that you have a working copy of Zope and ZPT (this tutorial uses Zope 2.5.0, which comes with ZPT built in), and can log in to the Zope management interface. In case you can't, drop by http://www.zope.org/, get yourself set up and come back when you're ready to roll.
The Right Choice
Let's start with a very basic, but important, question: why use ZPT when you can use DTML?
There are a couple of reasons why you might prefer to use ZPT instead of DTML. First, DTML isn't really all that friendly, even to developers who are used to wading in the muddy waters of open-source programming languages. The language comes with numerous idiosyncrasies, which can be both annoying and confusing to developers. For example, the result value obtained from a DTML variable often depends on the manner in which it is accessed, and the connectivity between DTML and Python can often leave you confused and desperately searching for a Python programmer to help you get unstuck.
In addition to the technical problems with DTML, it's also not perfect when it comes to separating business logic from interface code. Since DTML documents contain both HTML code and DTML commands, interface designers and developers must collaborate closely with each other whenever a change needs to be made to the interface or processing sequences within a DTML document. Obviously, this means more time and effort to implement changes.
ZPT attempts to resolve this last problem by using templates to separate presentation and layout information from program code. This two-tiered approach affords both developers and designers a fair degree of independence when it comes to maintaining a Zope application, and can substantially reduce the time and effort required in the post-release phases of a development project.
Does this mean DTML is now redundant? No, not really. ZPT should not be considered a replacement for DTML; rather, it should be treated as an alternative to DTML. There still are some things that can be handled only using DTML - sending mail, managing sequences, batching and so on. If you're planning on working with Zope, you're going to need to keep your DTML skills current. It's just that you now have a little more choice.
The Power Of Three
ZPT actually consists of three different components, which interact with each other to offer developers a fair amount of power and flexibility. Here they are:
TAL: First up is the Template Attribute Language, also referred to as TAL. As per the official TAL specification, TAL is "an attribute language used to create dynamic templates...it allows elements of a document to be replaced, repeated, or omitted." Put more simply, TAL is a template language which uses special attributes within HTML tags to perform different actions, like variable interpolation, tag repetition and content replacement.
METAL: Nope, this isn't the music that makes your head hurt and your eardrums pound. METAL is the Macro Expansion Template Attribute Language, and it is defined as "an attribute language for structured macro preprocessing". In ZPT, macros allow developers to create a single snippet of code and reuse it extensively across the application. The advantage: a change to the macro will be immediately reflected across the templates it has been used in.
TALES: Apart from having a familiar ring to it, this acronym actually stands for Template Attribute Language Expression Syntax. Officially, TALES "describes expressions that may be used to supply TAL and METAL with data." These expressions may be in the form of paths to specific Zope objects, Python code or strings, including strings containing variables to be substituted, and they form a significant component of ZPT.
Let's see what all this means with a quick look at some code.
What's In A Name?
Consider the following simple code snippet:
My name is <b tal:content="template/id">template id</b>.
If you were to render this through Zope, in a template with the ID "MyFirstTemplate", you'd see the following output:
My name is <b>MyFirstTemplate</b>.
What happened here? Roughly translated, the line
<b tal:content="template/id">template id</b>
means "replace the content enclosed within the
<b> element with the result obtained by evaluating the TALES expression [template/id]". When the template is rendered, this TALES expression will evaluate to the ID of the current page template, and will appear within the <b> tag.
The TALES expression
is a path expression pointing to a specific Zope object - in this case, the ID of the current template - and it provides an easy and intuitive way of accessing the value of any object within Zope. It need not be restricted to objects alone either - TALES expressions are equally versatile at evaluating string data or Python code.
TALES path expressions always begin with a variable name, and drill down hierarchically to the required object. A TALES path is traversed in a sequential manner until the final step is reached or until an error occurs. The result of the path expression is the value of the object found at the end of the path.
The "tal:content" attribute indicates to the Zope parser that only the content within the enclosing element should be replaced with the results of the TALES expression. TAL comes with a number of such special attributes, each with its own particular function - I'll be discussing them in detail over the course of this tutorial.
Note the special "tal:" namespace, which must prefix every TAL attribute, and which provides developers with an easy way to separate their own custom names from those defined in the TAL specification.
Usage of the TAL namespace also has one very important additional benefit, closely related to the developer/designer interaction problem discussed on the previous page. Most HTML editors will not understand the TAL namespace prefix or attributes, and will therefore ignore it when rendering a page; this allows designers to view and make changes to the HTML interface of a page without interfering with its business logic or requiring the intervention of developers (so long, obviously, as they don't fiddle with the special TAL statements embedded within the code).
Let's take a look at a more concrete example of how TAL works. Start up Zope and log in to the management interface with the user name and password that was created when you installed Zope.
As you should know by now, this is the tool that you will be using to build your Web applications. You can use it to create documents, folders, and your own Zope products. And the first step in this process is to create a folder to store all the objects we're going to be creating.
Create a Folder by selecting it from the drop-down menu that appears at the top of the page, and name it "ZPT Basics".
The next step is to navigate to the folder that you just created and add a Page Template to it, using the process outlined above.
Give this template an ID of "MyFirstTemplate", and a title of "Hello ZPT!".
As you may have guessed by now, the object ID that you assign at each stage is fairly important - it provides a unique identifier for the object within Zope, which can be used to access and manipulate it from your scripts.
A default page template will be created in the "ZPT Basics" folder. Replace the code within it with the following:
<html> <head> <title tal:content="template/title">The title</title> </head> <body> <h2>Welcome to <span tal:replace="here/title_or_id">content title or id</span></h2> This is a Page Template with ID <em tal:content="template/id">template id</em> and title <em tal:content="template/title">template title</em>. </body> </html>
Here's the output of this template (you can see this via the "Test" option in the Zope management interface),
and here's the corresponding HTML code generated by Zope.
<html> <head> <title>Hello ZPT!</title> </head> <body> <h2>Welcome to ZPT Basics</h2> This is a Page Template with ID <em>MyFirstTemplate</em> and title <em>Hello ZPT!</em>. </body> </html>
There are a number of TALES expressions in the code snippet shown in the previous page - here's the first one:
<title tal:content="template/title">The title</title>
As you've already seen, this means "replace the content enclosed within the
<title> element with the result obtained by evaluating the TALES expression [template/title]".
When the template is rendered, this TALES expression will evaluate to the title of the current page, which will appear within the
<title> tag, like this:
The second statement is similar, except that this time, the element itself is replaced, together with the content enclosed within it. When the page is rendered, the TAL statement
<h2>Welcome to <span tal:replace="here/title_or_id">content title or id</span></h2>
will replace the
<span>... tags with the results of evaluating the TALES expression within it, like this:
<h2>Welcome to ZPT Basics</h2>
Putting It All In Context
You'll remember that I told you, a while back, that every TALES path expression must begin with a variable name. The TALES specification defines a number of such variables - here's a list of the most important ones:
"nothing" - a null value
"repeat" - a built-in TALES variable used for repetition
"root" - the starting point for the object hierarchy
"here" - the current context
"template" - the template itself
"container" - the template's container
"request" - the request object
You've already seen the "template" context in action - now let's take a look at the "request" context, which contains information about the current HTTP request. Consider the following example,
You are currently viewing the URL <i tal:content="request/URL">URL comes here</i>.
which, when rendered, displays the current URL:
You are currently viewing the URL http://medusa:8080/ZPT%20Basics/MyFirstTemplate.
This context also comes in handy when you're building Web forms and need to access the values of the various form variables. Consider the following simple DTML Document:
Please enter the following details: <br> <form action="formHandler" method="POST"> <p> Your name <br> <input type="text" size="20" name="name"> <br> </p> <p> Your favourite sandwich filling <br> <input type="text" size="15" name="filling"> <br> </p> <input type="submit" name="submit" value="Hit Me"><br> </form>
This displays the following form to the user.
Next, create the form processing script, "formHandler", using a Page Template:
Hello, <span tal:replace="request/form/name">name here</span>. I like <span tal:replace="request/form/filling">filling type here</span> sandwiches too. Maybe we should exchange recipes.
Try it out - here's an example of what you might see:
The beauty of this example lies in its simplicity. I've used the "request" context and obtained access to the form variables simply by specifying the appropriate path - the "request/form/name" path for the form variable "name", and the "request/form/filling" path for the form variable "filling".
And that's about it for this first part of the series. In this article, I gave you a gentle introduction to the ZPT way of doing things, explaining the special "content" and "replace" TAL attributes. In the next segment, I'll be looking at a few more of TAL's special attributes, including the ones that let you define variables and write conditional statements. Make sure you tune in for that one...and until then, stay healthy!
Note: All examples in this article have been tested on Linux/i586 with Zope 2.5.0. Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!This article was first published on 14 Sep 2002.