4.3 Templates

As we have seen, a web resource consists of a template and a handler function, with any of the two but not both possibly being empty. Templates have a default extension of .dsp that can be changed in the Draco configuration file. An http request is always made to the full template name, including extension.

Templates are regular html files that contain special blocks that are marked by <% and %> markers. Currently there are three types of blocks.

Directive Blocks

Directive blocks are blocks starting with an "at" sign (@). They contain instructions for to the Draco parser to take some special action. Currently there is only one directive defined: the include directive. This directive tells Draco to include another file in the template. The file that is to be included is specified by a file or by an expr attribute. The file attribute should specify the name of the file to include, while the expr attribute should be a Python expression that is evaluates to a file name. File names can be absolute or relative path names. Absolute path names are taken relative to the document root of the website while relative path names are relative to the directory of the template.

The template fragment:

<%@ include file="/header.inc" %>

includes a file named header.inc from the document root of the web site.

Expresion Blocks

Expression blocks are blocks starting with an "equal" sign (=) and contain a single Python expression. The expression is evaluated by Draco and the result is substituted back into the template. All variables from the interface are available in the expression. If the expression evaluates to something that is not a string, the result is converted to a string by calling str().

Undefined variables in an expression block are initialized to the empty string. This is very handy when, for example, providing form feedback. You don't have to check first if a parameter was given, you can just evaluate its corresponding variable. The choice for the empty string was a deliberate one. It might seem more ``correct'' to initialize to None, but in an expression block that would evaluate to the string "None". An empty string in much more neutral in a template.

The example below displays the value of the variable status if that variable has been set:

<%= status %>

Code Blocks

Code blocks are blocks that are not marked by any special character. They contain a fragment of Python code that is run by Draco. The code can output results by using print or sys.stdout.write() and these results are substituted back into the template. As with expressions, all variables in the interface are available to the code fragment. Undefined variables in the code block are initialized to the empty string.

The example below display the string "Hello, world!":

<%
    print "Hello, world!"
%>

In code blocks, special care must be taken to indentation. You are likely using indentation for structuring your html, but, as we know, Python uses indentation for grouping too! Draco uses a simple but effective approach to indenting. The first nonempty line in a code block determines the indentation level and this many spaces are removed from each line in the block. If you use tabs, they are converted to 8 spaces before de-indenting. This is the same value that Python itself uses.

Note: Some web content systems have a way to ``escape to html mode''. This means that inside a code block you can go back to html mode, even when a language statement (like a for loop) is not complete. The statement is then ended inside another code block. This functionality is not available in Draco for two reasons. First, I found that code legibility did not improve with this and that a print statement with a multiline string was just the same thing. The second reason is more technical. Python doesn't have an "end of block" marker. Instead, the end of block is marked by going back one indentation level. This gets ugly in a template because you now have to compare indentation levels of two different code blocks. Some other web content systems using Python define a special "end of block" marker. I think this is ugly too because it is just what Python tries to eliminate.