16. Basic DTML
Attention
This document was written for Zope 2.
Note
DTML has been the primary markup language within Zope for a long time. However the recommended primary markup language within Zope is nowadays ZPT (Zope Page Templates). ZPT is your choice for generating markupish output like HTML or XML. The usage of DTML should be limited where you have to generate non-markupish output like text files or other formats. Since DTML is pretty old it really does not support features like internationalization or unicode very well. In addition the syntax of DTML is not always very easy to understand. You have to learn DTML to some point if you intend to use ZSQL methods (for RDBMS integration with Zope) - but even for the RDBMS integration we have better solutions like Object-Relational-Mappers (check with the chapter about relational database connectivity).
DTML (Document Template Markup Language) is a templating facility which supports the creation of dynamic HTML and text. In Zope it is most often used when you want to generate non-HTML or non-XML content, like parts of SQL queries, dynamic CSS and JavaScript files or email templates. Generating HTML and XML is usually done with page templates inside Zope.
DTML is a tag-based presentation and scripting language. This means that tags (e.g. ‘<dtml-var name>’) embedded in your text cause parts of your text to be replaced with “computed” content.
DTML is a “server-side” scripting language. This means that DTML commands are executed by Zope at the server, and the result of that execution is sent to your web browser. By contrast, “client-side” scripting languages like JavaScript are not processed by the server, but are rather sent to and executed by your web browser.
16.1. How DTML Relates to Similar Languages and Templating Facilities
DTML is similar in function to “HTML-embedded” scripting languages such as JSP, PHP, or mod_perl. It differs from these facilities inasmuch as it will not allow you to create “inline” Python statements (if… then.. else..) in the way that JSP, mod_perl or PHP will allow you to embed a block of their respective language’s code into a page. DTML does allow you to embed Python expressions (a == 1) into tags. It provides flow control and conditional logic by way of “special” tags. It is more similar to Perl’s ‘HTML::Template’ package than it is to mod_perl in this way. It can also be compared to the web server facility of Server Side Includes (SSI), but with far more features and flexibility.
16.2. When To Use DTML
If you don’t want to use page templates for whatever reason DTML might work well. Likewise, if you want to dynamically create non-HTML text (like CSS stylesheets or email messages), DTML can help.
16.3. When Not To Use DTML
If you want code which expresses a set of complex algorithms to be maintainable (as “logic” programming should be), you shouldn’t write it in DTML. DTML is not a general purpose programming language, it instead is a special language designed for formatting and displaying content. While it may be possible to implement complex algorithms in DTML, it is often painful.
For example, let’s suppose you want to output some text which displays a representation of the famous Fibonacci sequence. You would not want to write the program that actually makes the calculation of the Fibonacci numbers by writing DTML. It could be done in DTML, but the result would be difficult to understand and maintain. However, DTML is perfect for describing the output that the results of the Fibonnacci calculations are inserted into. You can “call out” from DTML to Script (Python) objects as necessary and process the results of the call in DTML. For example, it is trivial in Python (search for the word Fibonacci on this page) to implement a Fibonacci sequence generator, and trivial in DTML to create some dynamic output which shows these numbers in a readable format. If you find yourself creating complex and hard-to-understand logic in DTML, it’s likely time to explore the the Zope features which allow you to script “logic” in Python, while letting DTML do the presentation “dirty work”.
String processing is another area where DTML is likely not the best choice. If you want to manipulate input from a user in a complex way, by using functions that manipulate strings, you are better off doing it in Python, which has more powerful string processing capabilities than DTML.
16.4. The Difference Between DTML Documents and DTML Methods
You can use DTML scripting commands in two types of Zope objects, DTML Documents and DTML Methods. These two types of DTML objects are subtly different from one another, and their differences cause many would-be DTML programmers to become confused when deciding to use one versus the other. So what is the difference?
DTML Methods are used to carry out actions. They are presentation objects (as used in the vernacular of the Using Basic Zope Objects chapter). If you want to render the properties or attributes of another object like a DTML Document or a Folder, you will use a DTML Method. DTML Methods do not have their own properties.
DTML Documents are content objects (in the vernacular used in the chapter entitled Using Basic Zope Objects). If you want to create a “stand-alone” text document, you might create a DTML Document object to hold the text. DTML Document objects have their own properties (attributes), unlike DTML Methods.
In almost all cases, you will want to use a DTML Method object to perform DTML scripting. DTML Document objects are an artifact of Zope’s history that is somewhat unfortunate. In Zope’s earlier days, a consensus came about that it was important to have objects in Zope that could perform DTML commands but have properties of their own. At the time, the other content objects in Zope, such as Files and Images were either nonexistent or had limitations in functionality that made the concept of a DTML Document attractive. That attraction has waned as Zope’s other built-in content objects have become more functional. DTML Documents remain in Zope almost solely as a backwards-compatibility measure. If you never use a DTML Document in your work with Zope, you won’t miss out on much!
16.5. Details
DTML Methods are method objects. The chapter named Object Orientation discusses the concept of a “method”. DTML Methods are methods of the folder that contains them, and thus they do not have regard for their own identity as a Zope object when they are used. For example, if you had a folder called Folder and a DTML method in that folder called Method:
AFolder/
AMethod
AMethod is a method of AFolder. This means that AMethod does not have any of it’s own attributes or properties. Instead it uses those of AFolder. Suppose you put the following DTML string in AMethod:
<dtml-var id>
When you view the AMethod DTML Method, you will see the string ‘AFolder’, which is the ‘id’ of AMethod’s containing Folder (AFolder). When this DTML method is viewed, it resolves the name ‘id’ to the string which is the value of AFolder’s ‘id’ property.
DTML Documents, on the other hand, are not methods. They are “aware” of their own identity as Zope objects. For example, if you created a DTML Document in the folder AFolder called ADocument, and you put the above DTML string into ADocument and viewed it, it would render to the string ‘ADocument’. It resolves the name ‘id’ to the string which is the value of its own id, not the id of its containing folder.
Important
For this chapter, unless stated otherwise, use DTML Methods to hold the example DTML text, as opposed to DTML Documents!**
16.6. DTML Tag Syntax
DTML contains two kinds of tags, singleton and block tags. Singleton tags consist of one tag enclosed by less-than (<) and greater-than (>) symbols. The var tag is an example of a singleton tag:
<dtml-var parrot>
There’s no need to close the var tag with a ‘</dtml-var>’ tag because it is a singleton tag.
Block tags consist of two tags, one that opens the block and one that closes the block, and content that goes between them:
<dtml-in mySequence>
this is a text inside the dtml-in tag block
</dtml-in>
The opening tag starts the block and the closing tag ends it. The closing tag has the same name as the opening tag with a slash preceding it. This is the same convention that HTML and XML use.
16.7. DTML Tag Names, Targets, and Attributes
All DTML tags have names. The name is simply the word which follows ‘dtml-’. For instance, the name of the DTML tag ‘dtml-var’ is ‘var’, and the name of the DTML tag ‘dtml-in’ is ‘in’.
Most DTML tags have targets. The target of a DTML tag is just the word or expression that, after a space, follows the tag name. For example, the target of the DTML tag ‘<dtml-var standard_html_header>’ is ‘standard_html_header’. The target of the DTML tag ‘<dtml-in foo>’ is ‘foo’. The target of the DTML tag ‘<dtml-var “objectIds()”> is the expression “objectIds()”. The target typically refers to the name of an object (or a Python expression that resolves to an object) that you wish the tag to operate upon.
All DTML tags have attributes. An attribute provides information about how the tag is supposed to work. Some attributes are optional. For example, the var tag inserts the value of its target. It has an optional missing attribute that specifies a default value in case the variable can’t be found:
<dtml-var wingspan missing="unknown wingspan">
If the wingspan variable is not found then ‘unknown wingspan’ is inserted instead.
Some attributes don’t have values. For example, you can convert an inserted variable to upper case with the upper attribute:
<dtml-var exclamation upper>
Here we are referencing the exclamation target, modifying it with the attribute upper. Notice that the upper attribute, unlike the missing attribute doesn’t need a value.
See the DTML Reference appendix for more information on the syntax of different DTML tags.
16.8. Creating a “Sandbox” for the Examples in This Chapter
You should create a Folder in your Zope’s root folder named “DTML_Examples” if you intend on creating objects from examples in this chapter. Create the example objects within this “sandbox”. This prevents you from littering your Zope root folder with DTML examples.
16.9. Examples of Using DTML for Common Tasks
Below, we show how to use DTML to complete three common tasks: inserting text into a web page, displaying results by iterating over a sequence, and processing form results.
16.9.1. Inserting Text into HTML with DTML
DTML commands are written as tags that begin with dtml-. You create dynamic content in DTML by mixing content and DTML tags together. Inserting the value of a variable (a variable is also known as a “target”) into HTML is the most basic task that you can perform with DTML. Many DTML tags insert variable values, and they all do it in a similar way. Let’s look more closely at how Zope inserts variable values.
Create a folder in your sandbox with the id “Feedbags” and the title “Bob’s Fancy Feedbags”. While inside the ‘Feedbags’ folder, create a DTML Method with an id of “pricelist”. Note: an ‘id’ is how you refer to an object such as a DTML Method or a Folder later on; titles are for informational purposes only.
Change the contents of the DTML Method to the following:
<dtml-var standard_html_header>
<h1>Price list for <dtml-var title></h1>
<p>Hemp Bag $2.50</p>
<p>Silk Bag $5.00</p>
<dtml-var standard_html_footer>
Now view the DTML Method by clicking the View tab. When you view the DTML method this way, it will be rendered, which means that you will not necessarily see a straight representation of the HTML that you typed in to the form. Instead you will see the rendered version of the page, which will include the extra text provided by DTML by way of the tags you’ve inserted. You should see something like the figure below:
If you tell your browser to view the HTML source of the Workspace frame, you will see something not unlike the below:
<html>
<head><title>Bob's Fancy Feedbags</title>
</head>
<body bgcolor="#FFFFFF">
<h1>Price list for Bob's Fancy Feedbags</h1>
<p>Hemp Bag $2.50</p>
<p>Silk Bag $5.00</p>
</body>
</html>
That’s certainly not what you typed in, is it?
DTML makes the reuse of content and layout possible. In the example above, we’ve made use of the ‘standard_html_header’ DTML Method and the ‘standard_html_footer’ DTML Method, both of which live in the root folder, to insert HTML text into our page. These DTML methods (and any other DTML method) can be used by other DTML methods to insert text into our rendered output.
We’ve seen that DTML inserts an HTML header, an HTML footer, and a title into the web page. But how does the “var” tag find the values that it inserts in place of “standard_html_header”, “title” and “standard_html_footer”?
DTML name lookup is somewhat “magical”, because you don’t need to explicitly tell DTML where to find a variable. Instead, it tries to guess what you mean by following a preordained set of search rules. DTML gets the values for variable names by searching an environment which includes the current object, the containment path, and request variables like values submitted by a form and cookies. The DTML Name Lookup Rules represent the namespaces searched and their relative precedence. As an example, let’s follow the ‘pricelist’ DTML code step-by-step. In our ‘pricelist’ method, we’ve asked DTML to look up three names: “standard_html_header”, “title”, and “standard_html_footer”. It searches for these variables in the order that they are mentioned in the page.
DTML looks first for “standard_html_header”. It looks in the “current object” first, which is its container, the ‘Feedbags’ folder. The ‘Feedbags’ folder doesn’t have any methods or properties or sub-objects by that name. Next Zope tries to acquire the object from its containers. It examines the ‘Feedbags’ folder’s container (your sandbox folder, likely named “DTML_Examples”), which also doesn’t turn up anything. It continues searching through any intermediate containters, which also don’t have a method or property named “standard_html_header” unless you’ve put one there. It keeps going until it gets to the root folder. The root folder does have a sub-object named “standard_html_header”, which comes as a default object in every Zope. The ‘standard_html_header’ object is a DTML Method. So Zope calls the ‘standard_html_header’ method in the root folder and inserts the results into the page. Note that once DTML finds a property or variable, if it is callable (as in the case of a DTML Method, an External Method, a SQL Method, or a Script (Python) object), it is called and the results of the call are inserted into the page.
Next DTML looks for the name “title”. Here, the search is shorter. On its first try, DTML finds the ‘Feedbags’ folder’s ‘title’ property and inserts it. The ‘title’ property is not a method or a script, so DTML doesn’t need to call it. It just renders it into the output.
Finally DTML looks for the name standard_html_footer. It has to search all the way up to the root folder to find it, just like it looked for standard_html_header. It calls the standard_html_footer in the root and inserts the text result.
The resulting page is fully assembled (rendered) at this point, and is sent to your browser.
Understanding how DTML looks up variables is important. We will explore the DTML name lookup mechanism further in the chapter entitled Variables and Advanced DTML. It is also documented in Appendix E.
16.9.2. Formatting and Displaying Sequences
It is common that people want to use DTML to format and display sequences. A sequence is just a list of items, like “Fred, Joe, Jim”. Often, you want to create an HTML table or a bulleted list that contains elements in a sequence. Let’s use DTML to call out to an object which returns a sequence and render its result.
Create a Script (Python) object named “actors” in your sandbox folder. Give the script the following body and save it:
## Script (Python) "actors"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=
##
return ['Jack Lemmon', 'Ed Harris','Al Pacino', 'Kevin Spacey', 'Alan Arkin']
Make sure that all of the lines of this script line up along the left-hand side of the textarea to avoid receiving an error when you attempt to save the script, since Python is sensitive to indentation. Don’t worry about the ‘##’s for now, we will explain these later.
This Script (Python) object returns a Python data structure which is a list of strings. A list is a kind of sequence, which means that DTML can iterate over it using the dtml-in tag. Now create a DTML Method named “showActors” in your sandbox, give it this body, and save it:
<html>
<body>
<h1>Actors in the movie Glengarry Glen Ross</h1>
<table border="1">
<th>Name</th>
<dtml-in actors>
<tr>
<td><dtml-var sequence-item></td>
</tr>
</dtml-in>
</table>
</body>
</html>
The DTML in tag iterates over the results of the actors script and inserts a table row into a table for each of the actors mentioned in the script. Note that inside the table cell, we use a special name sequence-item. sequence-item is a special name that is meaningful within a dtml-in tag. It refers to the “current item” (in this case, the actor name string) during processing. The HTML source of the Workspace frame when you click the View tab on the ‘showActors’ method will look something like:
<html>
<body>
<h1>Actors in the movie Glengarry Glen Ross</h1>
<table border="1">
<th>Name</th>
<tr>
<td>Jack Lemmon</td>
</tr>
<tr>
<td>Ed Harris</td>
</tr>
<tr>
<td>Al Pacino</td>
</tr>
<tr>
<td>Kevin Spacey</td>
</tr>
<tr>
<td>Alan Arkin</td>
</tr>
</table>
</body>
</html>
Note that you didn’t have to specifically tell DTML that you are querying a Script (Python) object. You just tell it the name of the object to call (in this case ‘actors’), and it does the work of figuring out how to call the object and pass it appropriate arguments. If you replace the ‘actors’ Script with some other kind of object that does exactly the same thing, like another DTML Method, you won’t have to change your ‘showActors’ DTML Method. It will “just work”.
16.9.3. Processing Input from Forms
You can use DTML to perform actions based on the information contained in the submission of an HTML form.
Create a DTML Method named “infoForm” with the following body:
<dtml-var standard_html_header>
<p>Please send me information on your aardvark adoption
program.</p>
<form action="infoAction">
name: <input type="text" name="user_name"><br>
email: <input type="text" name="email_addr"><br>
<input type="submit" name="submit" value=" Submit ">
</form>
<dtml-var standard_html_footer>
This is a web form that asks the user for information, specifically his user name and email address. Note that you refer to the name “infoAction” as the action of the HTML form. This really has nothing to do with DTML, it’s an attribute of the HTML form tag. But the name specified in the form action tag can name another Zope object which will receive and process the results of the form when it is submitted.
Create a DTML Method named infoAction in the same folder as the ‘infoForm’ method. This is the target of the ‘infoForm’ form action. This method will display a bland “thanks” message which includes the name and email information that was gathered from the web form. Provide the infoAction method with the following body and save it:
<dtml-var standard_html_header>
<h1>Thanks <dtml-var user_name></h1>
<p>We received your request for information and will send you
email at <dtml-var email_addr> describing our aardvark adoption
program as soon as it receives final governmental approval.
</p>
<dtml-var standard_html_footer>
Navigate back to the ‘infoForm’ method and use the View tab to execute it. Fill out the form and click the Submit button. If all goes well you should see a thank you message that includes your name and email address, much like the figure below:
The Zope object named REQUEST contains information about the current web request. This object is in the DTML name lookup path. The ‘infoAction’ method found the form information from the web request that happened when you clicked the submit button on the rendering of ‘infoForm’. DTML looks for variables in the current web request, so you can just refer to the form variable names in the target method by name. In our case, we were able to display the values of the form elements user_name and email_addr in the ‘infoAction’ method just by referring to them by name in their respective dtml-var tags. DTML used its lookup rules to search for the variable names. It found the names in the “REQUEST.form” namespace and displayed them. If it had found an object with either name email_addr or user_name earlier in the lookup (if perhaps there was a Zope object in your acquisition path named ‘user_name’) it would have found this object first and rendered its results. But, mostly by chance, it didn’t, and found the name in REQUEST instead.
Let’s examine the contents of the Zope REQUEST object in order to shed more light on the situation. Create a new DTML Method object named ‘show_request’ in your sandbox folder. Give it the the following body:
<dtml-var REQUEST>
The ‘show_request’ method will render a human-readable representation of Zope’s REQUEST object when you click submit on the ‘infoForm’ rendering. Visit the ‘infoForm’ method, and change it to the following:
<dtml-var standard_html_header>
<p>Please send me information on your aardvark adoption
program.</p>
<form action="show_request">
name: <input type="text" name="user_name"><br>
email: <input type="text" name="email_addr"><br>
<input type="submit" name="submit" value=" Submit ">
</form>
<dtml-var standard_html_footer>
We changed the form action of the ‘infoForm’ method to show_request. Now click the View tab of the new ‘infoForm’ method. Fill in some information in the form elements, and click Submit. You will see something like the following:
form
submit ' Submit '
email_addr 'chrism@zope.com'
user_name 'Chris'
cookies
tree-s 'eJzTiFZ3hANPW/VYHU0ALlYElA'
lazy items
SESSION <bound method SessionDataManager.getSessionData of <SessionDataManager instance at 897d020>
other
AUTHENTICATION_PATH ''
user_name 'Chris'
PUBLISHED <DTMLMethod instance at 8a62670>
submit ' Submit '
SERVER_URL 'http://localsaints:8084'
email_addr 'chrism@zope.com'
tree-s 'eJzTiFZ3hANPW/VYHU0ALlYElA'
URL 'http://localsaints:8084/DTML_Example/show_request'
AUTHENTICATED_USER admin
TraversalRequestNameStack []
URL0 http://localsaints:8084/DTML_Example/show_request
URL1 http://localsaints:8084/DTML_Example
URL2 http://localsaints:8084
BASE0 http://localsaints:8084
BASE1 http://localsaints:8084
BASE2 http://localsaints:8084/DTML_Example
BASE3 http://localsaints:8084/DTML_Example/show_request
environ
SCRIPT_NAME ''
HTTP_ACCEPT_ENCODING 'gzip, deflate, compress;q=0.9'
SERVER_PORT '8084'
PATH_TRANSLATED '/DTML_Example/show_request'
HTTP_ACCEPT 'text/xml...'
GATEWAY_INTERFACE 'CGI/1.1'
HTTP_COOKIE 'tree-s="eJzTiFZ3hANPW/VYHU0ALlYElA"'
HTTP_ACCEPT_LANGUAGE 'en-us, en;q=0.50'
REMOTE_ADDR '192.168.1.3'
SERVER_NAME 'saints'
HTTP_USER_AGENT 'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.1a+)'
HTTP_ACCEPT_CHARSET 'ISO-8859-1, utf-8;q=0.66, *;q=0.66'
CONNECTION_TYPE 'keep-alive'
channel.creation_time 1027876407
QUERY_STRING 'user_name=Chris&email_addr=chrism%40zope.com&submit=+Submit+'
SERVER_PROTOCOL 'HTTP/1.1'
HTTP_KEEP_ALIVE '300'
HTTP_HOST 'localsaints:8084'
REQUEST_METHOD 'GET'
PATH_INFO '/DTML_Example/show_request'
HTTP_REFERER 'http://localsaints:8084/DTML_Example/infoForm'
You have instructed the ‘show_request’ method to render the contents of the web request initiated by the ‘infoForm’ method. Note that each section (form, cookies, lazy items, other, and environ) represents a namespace inside the REQUEST. DTML searches all of these namespaces for the names you refer to in your ‘infoForm’ form. Note that email_addr and user_name are in the “form” namespace of the REQUEST. There is lots of information in the rendering of the REQUEST, but for us, this is the most pertinent. For more information on the REQUEST object, visit the Zope Help system, and choose Zope Help -> API Reference -> Request.
16.9.4. Dealing With Errors
Let’s perform an experiment. What happens if you try to view the ‘infoAction’ method you created in the last section directly, as opposed to getting to it by submitting the ‘infoForm’ method? Click on the ‘infoAction’ method and then click the View tab. You will see results not unlike those in the figure below.
Zope couldn’t find the user_name variable since it was not in the current object, its containers or the web request. This is an error that you’re likely to see frequently as you learn Zope. Don’t fear, it just means that you’ve tried to insert a variable that Zope can’t find. You can examine the error by visiting the error_log object in your root folder. In this case, we know why the error occurred, so visiting the error in the error_log isn’t really necessary. In this example, you need to either insert a variable that Zope can find, or use the ‘missing’ attribute on the var tag as described above:
<h1>Thanks <dtml-var user_name missing="Anonymous User"></h1>
Understanding where DTML looks for variables will help you figure out how to fix this kind of problem. In this case, you have viewed a method that needs to be called from an HTML form like infoForm in order to provide variables to be inserted in the output.
16.9.5. Dynamically Acquiring Content
Zope looks for DTML variables in the current object’s containers (its parent folders) if it can’t find the variable first in the current object. This behavior allows your objects to find and use content and behavior defined in their parents. Zope uses the term acquisition to refer to this dynamic use of content and behavior.
An example of acquisition that you’ve already seen is how web pages use standard headers and footers. To acquire the standard header just ask Zope to insert it with the var tag:
<dtml-var standard_html_header>
It doesn’t matter where the ‘standard_html_method’ object or property is located. Zope will search upwards in the object database until it finds the ‘standard_html_header’ that is defined in the root folder.
You can take advantage of how Zope looks up variables to customize your header in different parts of your site. Just create a new ‘standard_html_header’ in a folder and it will override global header for all web pages in your folder and below it.
Create a new folder in your “sandbox” folder with an id of “Green”. Enter the ‘Green’ folder and create a DTML Method with an id of “welcome”. Edit the ‘welcome’ DTML Method to have these contents:
<dtml-var standard_html_header>
<p>Welcome</p>
<dtml-var standard_html_footer>
Now view the ‘welcome’ method. It should look like a simple web page with the word welcome, as shown in the figure below.
Now let’s customize the header for the Green folder. Create a DTML Method in the Green folder with an id of “standard_html_header”. Give it the following body:
<html>
<head>
<style type="text/css">
body {color: #00FF00;}
p {font-family: sans-serif;}
</style>
</head>
<body>
Notice that this is not a complete web page. For example, it does not have an ending ‘</html>’ tag. This is just a fragment of HTML that will be used as a header, meant to be included into other pages. This header uses CSS (Cascading Style Sheets) to make some changes to the look and feel of web pages.
Now revisit the ‘welcome’ method and click its View tab again. You will see something like the figure below:
The rendering now looks quite different. This is because it is now using the new header we introduced in the ‘Green’ folder. This header will be used by all web pages in the ‘Green’ folder and its sub-folders.
You can continue this process of overriding default content by creating another folder inside the ‘Green’ folder and creating a ‘standard_html_header’ DTML Method there. Now web pages in the sub-folder will use their local header rather than the ‘Green’ folder’s header. You can of course also create a ‘standard_html_footer’, providing it with local content as well.
Using this pattern you can quickly change the look and feel of different parts of your website. If you later decide that an area of the site needs a different header, just create one. You don’t have to change the DTML in any of the web pages; they’ll automatically find the closest header and use it.
16.9.6. Using Python Expressions from DTML
So far we’ve looked at simple DTML tags. Here’s an example:
<dtml-var getHippo>
This will insert the value of the variable named getHippo, whatever that may be. DTML will automatically take care of the details, like finding the object which represents the variable and calling it if necessary. We call this basic tag syntax name syntax to differentiate it from expression syntax.
When you use DTML name syntax, DTML tries to do the right thing to insert the results of the object looked up by the variable name, no matter what that object may be. In general this means that if the variable is another DTML Method or DTML Document, it will be called with appropriate arguments. However, if the variable is not another DTML Method or DTML Document, and it requires parameters, you need to explicitly pass the arguments along using an expression.
Expressions used in DTML allow you to be more explicit about how to find and call variables. Expressions are tag attributes that contain small snippets of code in the Python programming language. These are typically referred to as Python expressions.
A Python expression is essentially any bit of code that is not a Python statement. For example, the Python statement ‘a = 1’ assigns “1” to the “a” variable. You cannot use this statement in DTML expressions. Likewise, you cannot use the statement ‘print “x”’ in DTML. It is not an expression. Essentially, an expression must be a combination of values, variables, and Python operators. To find out more about Python’s expression syntax, see the Python Tutorial at the Python.org website.
An expression always results in a return value. For example, the Python expression “a == 5” returns the integer 1 if “a” is equal to the integer 5 or the integer 0 if “a” is not equal to the integer 5. The return value of an expression is used by DTML as the target of the DTML command.
The primary difference in DTML between using expressions as targets and names as targets is that DTML does some magic after it locates a named targets that it does not do after it finds an expression targets. For example, after finding object with the name ‘standard_html_header’ in the root folder via the name-syntax DTML command ‘<dtml-var standard_html_header>’, DTML calls the ‘standard_html_header’ object, inserting the results into the page. However, when you use an expression-syntax DTML command, like ‘<dtml-var expr=”standard_html_header”>’, DTML will not call the ‘standard_html_header’ object. Instead it will return a representation of the object as a string. In order to call the ‘standard_html_header’ object in an expression-syntax DTML tag, you need to do it explicitly by passing along arguments. When you delve into the realm of DTML expression syntax, DTML “magic” goes away, and you need to become aware of the arguments accepted by the target (if any) and pass them along.
Let’s create a Script (Python) object named ‘getHippo’ that must be called in DTML with expression syntax, because it takes a non-optional argument that named DTML syntax cannot provide.
Create a Script (Python) in your sandbox folder named getHippo. Provide it with the following body:
## Script (Python) "getHippo"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=trap
##title=
##
return 'The hippo was captured with a %s.' % trap
Note that this Script (Python) object takes a single parameter named “trap”. It is not an optional parameter, so we need to pass a value in to this script for it to do anything useful.
Now let’s make a DTML method to call ‘getHippo’. Instead of letting DTML find and call getHippo, we can use an expression to explicitly pass arguments. Create a DTML method named showHippo and give it the following body:
<dtml-var expr="getHippo('large net')">
Here we’ve used a Python expression to explicitly call the ‘getHippo’ method with the string argument, ‘large net’. View the ‘showHippo’ DTML Method. It will return a result not unlike the following:
The hippo was captured with a large net.
To see why we need to use expression syntax to call this script, let’s modify the ‘showHippo’ method to use DTML name syntax:
<dtml-var getHippo>
View the method. You will receive an error not unlike the following:
Error Type: TypeError
Error Value: getHippo() takes exactly 1 argument (0 given)
The ‘getHippo’ method requires that you pass in an argument, ‘trap’, that cannot be provided using DTML name syntax. Thus, you receive an error when you try to view the ‘showHippo’ method.
Expressions make DTML pretty powerful. For example, using Python expressions, you can easily test conditions:
<dtml-if expr="foo < bar">
Foo is less than bar.
</dtml-if>
Without expressions, this very simple task would have to be broken out into a separate method and would add a lot of overhead for something this trivial.
Before you get carried away with expressions, take care. Expressions can make your DTML hard to understand. Code that is hard to understand is more likely to contain errors and is harder to maintain. Expressions can also lead to mixing logic in your presentation. If you find yourself staring blankly at an expression for more than five seconds, stop. Rewrite the DTML without the expression and use a Script to do your logic. Just because you can do complex things with DTML doesn’t mean you should.
16.9.6.1. DTML Expression Gotchas
Using Python expressions can be tricky. One common mistake is to confuse expressions with basic tag syntax. For example:
<dtml-var objectValues>
and:
<dtml-var expr="objectValues">
These two examples if you are to put them in a DTML Method will end up giving you two completely different results. The first example of the DTML var tag will automatically call the object which is represented by objectValues.
In an expression, you have complete control over the variable rendering. In the case of our example, objectValues is a method implemented in Python which returns the values of the objects in the current folder. It has no required arguments. So:
<dtml-var objectValues>
will call the method. However:
<dtml-var expr="objectValues">
will not call the method, it will just try to insert it. The result will be not a list of objects but a string such as ‘<Python Method object at 8681298>’. If you ever see results like this, there is a good chance that you’re returning a method, rather than calling it.
To call a Python method which requires no arguments from an expression, you must use standard Python calling syntax by using parenthesis:
<dtml-var expr="objectValues()">
The lesson is that if you use Python expressions you must know what kind of variable you are inserting and must use the proper Python syntax to appropriately render the variable.
Before we leave the subject of variable expressions we should mention that there is a deprecated form of the expression syntax. You can leave out the “expr=” part on a variable expression tag. But please don’t do this. It is far too easy to confuse:
<dtml-var aName>
with:
<dtml-var "aName">
and get two completely different results. These “shortcuts” were built into DTML long ago, but we do not encourage you to use them now unless you are prepared to accept the confusion and debugging problems that come from this subtle difference in syntax.
16.11. Summary
DTML is a powerful tool for creating dynamic content. It allows you to perform fairly complex calculations. In the chapter entitled Variables and Advanced DTML, you’ll find out about many more DTML tags, and more powerful ways to use the tags you already have seen. Despite its power, you should resist the temptation to use DTML for complex scripting. In the chapter entitled Advanced Zope Scripting you’ll find out about how to use Python for scripting business logic.