DTML Basics (part 2)

Find out more about DTML's conditional expressions and comparison operators.

The Road Ahead

In the first part of this article, I explained the basics of DTML, giving you a crash course in objects, methods, variables and acquisition. I showed you how to use the Zope management interface to construct your own objects and methods, and also demonstrated how Zope looks for variables. Finally, I showed you how to use Zope's variable-formatting routines and structured text rulesets to format and manipulate output.

In this second tutorial, I will be delving a little deeper into DTML, looking closely at the constructs it offers to add decision-making logic to your code. I'll be examining conditional expressions and comparison operators, and demonstrating, with practical examples, how they can be used to craft more intelligent code. So let's get going!

If Only...

The first - and simplest - decision-making routine is the "if" statement, which generally looks like this:

<dtml-if id>
do this!
</dtml-if>

The "condition" here refers to a conditional expression, which evaluates to either true or false.

Let's look at a piece of code to understand this better. Here's a DTML Method named "ifOnly", created in the same "DTML Basics" folder I used last time.

<dtml-if id>
<p>Variable "id" exists and has the value <dtml-var id>.</p>
</dtml-if>

<dtml-if myName>
<p>Variable "myName" exists and has the value "<dtml-var myName>".</p>
</dtml-if>

Now, when you view the output of this Method, you should see the following:

Variable "id" exists and has the value DTML Basics.

If you take a close look at the code above, you'll see that it includes two "if" statements; however, only one is displayed in the output. This is because the first statement evaluated to a true value, because the variable "id" exists and has a value (the name of the container within which the DTML Method is stored). However, the second DTML variable "myName" does not exist. As a result, the corresponding "if" statement evaluates as false, and so, the text enclosed within the <dtml-if> block is not displayed.

Comparing Apples And Oranges

The example you've just seen was very rudimentary. To really add some punch, you need to know how to construct what the geeks call a conditional statement. And the very basis of a conditional statement is comparison - for example, "if this is equal to that, do thus and such".

Since Zope is built around Python, and Python comes with a bunch of useful operators designed specifically for use in conditional statements, you can use Python operators to build conditional statements in your DTML. Here's a list:

Assume $delta = 12 and $omega = 9

Operator What It Means Expression Evaluates To
== is equal to $delta == $omega False
!= is not equal to $delta != $omega True
> is greater than $delta > $omega True
< is less than $delta < $omega False
>= is greater than/equal to $delta >= $omega True
<= is less than/equal to $delta <= $omega False

These comparison operators can be used for both strings and numbers. A positive result returns true (1), while a negative result returns false (0).

Here's an example, a DTML Method named "access", which illustrates how these can be used:

<dtml-if expr="name == 'neo'">
<font face="Arial" size="-1">
Welcome to the Matrix, Neo. Access granted.
</font>
</dtml-if>

<dtml-if expr="name != 'neo'">
<font face="Arial" size="-1">
I wonder if you've heard of Shakespeare, <dtml-var name>.
<p>He postulated that a rose by any other name would smell just as sweet.</p>
<p>Unfortunately for you, I disagree. Access denied.</p>
</font>
</dtml-if>

Now, if you view this example in your browser, you'll see an ugly Zope error - this is because Zope cannot locate the variable "name", despite its best attempts to acquire it from the global namespace. Therefore, in order to see this script work properly, you need to first make sure that a variable named "name" actually exists. You can do this by adding this variable to the REQUEST object, via the URL GET method; just call the DTML script using a URL similar to this one:

http://localhost:8080/DTML%20Basics/access?name=Joe

Here's the output:.

I wonder if you've heard of Shakespeare, Joe.

He postulated that a rose by any other name would smell just as sweet.

Unfortunately for you, I disagree. Access denied.

Flip it around to see what it looks like when the variable named "name" holds the value "neo". Here's the URL to use,

http://localhost:8080/DTML%20Basics/access?name=neo

and here's the output:

Welcome to the Matrix, Neo.

In this case, since the conditional expression in the "if" statement evaluates to true, the output displayed by the script will be different.

There's one more thing that needs to be explained in the code above - the "expr" keyword. Normally, you can directly test whether or not a variable exists using the <dtml-var> or the <dtml-if> tags (as I've done in my very first example). However, if you want to do anything more complicated - for example, evaluate a DTML expression such as

"name == 'neo'"

or

"temperature > 98.6"
  • you need to first tell the DTML interpreter that you're talking about an expression and not a variable. And that's where "expr" comes in - it lets Zope know that it's dealing with a conditional expression, and not just a regular variable.

Welcome To The Matrix

In addition to the "if" statement, DTML also offers the "if-else" construct, used to define a block of code that gets executed when the conditional expression in the "if" statement evaluates as false.

The "if-else" construct looks like this:

<dtml-if condition-is-true>
do this!
<dtml-else>
do this!
</dtml-if>

As you can see, this construct can be used to great effect in the last example - instead of two separate "if" statements, we can combine them into a single "if-else" statement.

<dtml-if expr="name == 'neo'">

<font face="Arial" size="-1">
Welcome to the Matrix, Neo. Access granted.
</font>

<dtml-else>

<font face="Arial" size="-1">
I wonder if you've heard of Shakespeare, <dtml-var name>.
<p>He postulated that a rose by any other name would smell just as sweet.</p>
<p>Unfortunately for you, I disagree. Access denied.</p>
</font>

</dtml-if>

Don't take my word for it. Fire up your browser and see for yourself.

Celluloid Dreams

For situations which aren't entirely black and white, DTML also allows for multiple conditional branches, via the "if-elif-else" construct. A typical "if-elif-else" statement block would look like this:

<dtml-if "first condition is true">
do this!
<dtml-elif "second condition is true">
do this!
<dtml-elif "third condition is true">
do this!
... and so on ...
<dtml-else>
do this!
</dtml-if>

And here's an example that demonstrates how to use it. Create a DTML Document named "selectDay".

<html>
<head>
<style type="text/css">
td {font-family: Arial;}
</style>
</head>

<body>

<font face="Arial" size="+2">
Movie Schedule
</font>

<form method="POST" action="showMovieForTheDay">
<table cellspacing="5" cellpadding="5" border="0">

<tr>
<td align="center">
Pick a day
</td>
<td align="right">
<select name="day">
<option value="Monday">Monday
<option value="Tuesday">Tuesday
<option value="Wednesday">Wednesday
<option value="Thursday">Thursday
<option value="Friday">Friday
<option value="Saturday">Saturday
<option value="Sunday">Sunday
</select>
</td>
</tr>

<tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="Hit me!">
</td>
</tr>

</table>
</form>
</body>

</html>

As you can see, this is simply a form which allows you to pick a day of the week.

The real work is done by the DTML Method "showMovieForTheDay".

<html>
<head>
<basefont face="Arial">
</head>

<body>
Here is the movie for <dtml-var day> on your favorite channel:

<br>
<dtml-if expr="day == 'Monday'">
 <p><b>Dead By Monday</b> - directed by <b>Curt Truninger</b>
<dtml-elif expr="day == 'Tuesday'">
 <p><b>Never On Tuesday</b> - directed by <b>Adam Rifkin</b></p>
<dtml-elif expr="day == 'Wednesday'">
 <p><b>Any Given Wednesday</b> - directed by <b>Neil Mandt</b></p>
<dtml-elif expr="day == 'Thursday'">
 <p><b>Thursday Afternoon</b> - directed by <b>Clay Westervelt</b></p>
<dtml-elif expr="day == 'Friday'">
 <p><b>Friday The 13th</b> - directed by <b>Sean S. Cunningham</b></p>
<dtml-else>
 <p><b>Hey, Mr. Couch Potato! It's the weekend...go out and get some exercise!</b></p>
</dtml-if>
<br>

</body>
</html>

In this case, we've used the "if-elif-else" control structure to display the appropriate move for the day.

There's one important point to be noted here: as soon as one of the "if" statements within the block is found to be true, Zope will execute the corresponding code, skip the remaining "if" statements in the block, and jump immediately to the lines following the entire "if-elif-else" block.

Paring It Down

In certain situations, you can reduce the amount of code in your DTML Methods by using the "unless" conditional test, which looks like this:

<dtml-unless condition>
    do this!
</dtml-unless>

In this case, the statements in the "unless" block are only executed if the conditional statement evaluates to false. Consider the following rewrite of an earlier example, which demonstrates:

<dtml-unless expr="name != 'neo'">
<font face="Arial" size="-1">
Welcome to the Matrix, Neo.
</font>
</dtml-unless>

Deeper And Deeper

For the adventurous amongst you, DTML also lets you "nest" conditional statements - for example, this is perfectly valid DTML code.

<dtml-if expr="day == 'Thursday'">
    <dtml-if "time == '12'>
                <dtml-if "place == 'Italy'">
                       How about some pasta for lunch?
        </dtml-if>
    </dtml-if>
</dtml-if>

But you'll agree that is both complex and frightening. And so, in addition to the comparison operators I've used so liberally thus far, Python also provides the "and", "or" and "not" logical operators which allow you to group conditional expressions together. The following table should make this clearer.

Assume delta = 12, gamma = 12 and omega = 9

delta == gamma and delta > omega         True

delta == gamma and delta < omega         False

delta == gamma or delta < omega          True

delta > gamma or delta < omega           False

not delta                       False

Given this knowledge, it's a simple matter to rewrite the example above in terms of logical operators:

<dtml-if expr="(day == 'Thursday') and (time == '12') and (place == 'Italy')">
    How about some pasta for lunch?
</dtml-if>

Submitting To The King

You'll have noticed that in all the examples shown thus far, we've used two Zope objects - a single DTML Document containing the form, and a separate DTML Method which processes the form input and generates appropriate output. However, DTML provides an elegant method to combine those two pages into one via the "submit" variable.

In order to better understand this, create a new DTML Document named "DualPurposeForm" and add the following DTML code to it:

<dtml-var standard_html_header>

<dtml-if submit>

Welcome to Earth, <b><dtml-var species missing="Alien"></b> from the planet <b><dtml-var planet missing="Zorgo"></b>.
<p>
How was your journey? Travelling <b><dtml-var distance missing="so many"></b> light years must be quite a shock to the system. Why don't you relax and have a drink?

<dtml-else submit>

<form action="DualPurposeForm" method="POST">
Species:
<br>
<input name="species">
<p>
Home planet:
<br>
<input name="planet">
<p>
Distance (light years) from Earth:
<br>
<input name="distance">
<p>
<input type="Submit" name="submit" value="Beam Me Up, Scotty">
</form>

</dtml-if>

<dtml-var standard_html_footer>

In this case, the DTML "if" test will first check to see if the "submit" form variable exists. If it does, it implies that the form has been submitted, and the second half of the script, the actual form processor, comes into play. If it doesn't, it implies that the form has not yet been submitted, and so the initial, empty form is displayed.

This technique makes it possible to reduce the number of objects used, and perhaps make your Zope object collection easier to handle.

And that's about it for the moment. In this article, you learnt a little more about DTML, specifically about how to introduce decision-making logic into your Methods with DTML's numerous conditional statements. You learnt about the "if" and "unless" series of conditional statements, and saw a few examples of the different variants possible. Finally, you saw how all that theory can be put to practical use by implementing a simple form processor for use on a Web page.

In the next issue of DTML Basics, I'll be rounding out the fundamentals with a look at the various loop constructs provided by DTML. As with everything you've seen so far, DTML comes with its own twist (pardon the pun) on the traditional way of implementing loops. Come back next time to see what I'm talking about...and, until then, go practise.

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 on27 May 2002.