Section 13.1. Creating Dynamic Templates with Kid


13.1. Creating Dynamic Templates with Kid

Python has been home to any number of HTML templating schemes. Some have been copies of other models:

PSP --> ASP/JSP

Velocity --> Velocity (Java)

Mighty --> Mason (Perl)

Others were developed in Python first:

DTML

Cheetah

ZPT

STAN

Even though Kid might be new to the block, it isn't unaware of the past, and it isn't afraid to borrow from other template systems.

With that said, Kid was not designed to be just another text-based HTML template system. It's designed to make creation of XML documents easier.

Some of its predecessors such as PSP and Cheetah are more like string templates than XML template systems, so they can't provide any guarantee that they'll produce well-formed XML.

Kid is more like ZPT than any of its other predecessors. In fact, the main difference is that instead of creating a whole new language around metal and macros, Kid provides an easy way to embed full Python expressions in your XHTML.

This might seem like a small difference, but it makes the basics of Kid easy to learn for people who already know XHTML and Python. And the more complex pieces are actually easy to understand if you've ever had to do an XML transformation, and they are a whole heck of a lot more fun than XSLT.

Kid has good online documentation available at http://kid-templating.org/ that covers each of the language constructs in a lot more detail than we do here. But more important, if you are using Kid outside of TurboGears, the online docs explain how to use Kid's command-line tools to compile your templates, and do all the other things that TurboGears does for you behind the scenes.

13.1.1. Python-Powered Processing Directives

As I said, Python expressions are one of the most important elements in Kid. They enable you to import modules, classes, and functions into your template namespace. They enable you to do anything you can normally do in Python right there in your template. Some people think this is a bad idea, because they have used PHP, ASP, or JSP and ended up with all their business logic in their templates. And it's true you shouldn't do thatbut Kid is designed around the premise that you are the one who can make the best choices about what belongs in your template.

As you've already seen, all you have to do to embed Python directly into your template is to wrap it in <?python ?>. Then you can write something like this:

<?python def some_function()     pass ?>


One intentional limitation of the <?python ?> syntax can trip up users of PHP, ASP, and JSP. The results of a Python expression evaluated inside of the <?python ?> tag are not returned to the template output. You have to use Kid's content producing constructs for that.

This might seem like an arbitrary constraint, but it makes Kid's internal workings easier to understand. And even more important, it's just a good idea to keep whatever program logic needs to be in your template separate from the presentation logic.

13.1.2. Content-Producing Expressions

At the heart of Kid are several mechanisms to move data from the Python side of the world into Kid output. The first is the ${} syntax, which simply evaluates whatever Python expression is contained within {} and inserts the result into the template at that point. If you aren't using an expression, and just want to drop the value of a particular object into the template, you can even drop the curly brackets and just use a dollar sign before your object name like this: $name.

As mentioned before, this isn't particularly "designer friendly" when you use ${name} in the text of your page. When a designer opens up a page like this in Dreamweaver, they'll see ${name} directly, and it can be easy to accidentally enter a character where it doesn't belong and break the template. And whenever you're pulling longer items for insertion into the template, you rarely get a good picture of what the final output will look like by seeing ${story.text} in the template.

So, Kid provides a number of other useful content-producing constructs that make it easy to avoid those problems and make your templates even more designer friendly. The py:content="story.txt" processing directive tells Kid to replace the contents of the tag that it is on with the value of story.txt.

<p py:content="story.txt">This is replaced by the txt attribute of the story object</p>


One common Kid idiom is to use a <span> tag to hold some content you want placed into your template. The <span> tag allows you to have default text in the template when you preview it, and it doesn't impact the look of the final output. This is particularly useful for default content you want placed in the template you give to a designer, but which will be replaced programmatically in the final rendered output.

But sometimes you just don't want the <span> tag in the final outputin that case, you can add a py:strip="True" processing directive to the <span> tag, and it will be removed entirely from the final output. In this case, the content of the <span> tag is placed in the parent attribute.

In fact, the combination of py: and py:strip is so common that Kid provides a shortcut for it: py:replace. You can use the <span py:replace="story.txt">some text</span> to keep the processing directive hidden in a tag attribute (where it's less likely to be changed by a designer), you have default content in the template for better layout, and when the template is processed by Kid, the tag will be removed and replaced with the content's story.txt.

There's one last "content"-producing processing directive in Kid, which is used to add attributes rather than text nodes. If you pass some properly formatted object to the py:attr directive, those objects will automatically be turned into attributes for the tag element. You can pass a dictionary, a list of tuples in the form (name, value), or set of comma-separated keyword values in the form "id=story.id', align='right'". All of these will be turned into attributes with the proper names and values.

Note

All Kid files must be valid XML, which means that you always have to close your tags, and you have to escape any special characters. In particular, the < sign should always be substituted with &lt or &#60. Fortunately, Kid takes care of all that for you and escapes all the special characters that might be inserted into your template from the Python side of the world. So, your strings can all be blissfully unaware of the special needs of XML documents, and when you hand them off to Kid, everything will "just work."


13.1.3. Control Structures

Of course, no template system would be complete without conditional display and looping. Kid provides these through the py:if, and py:for processing directives. If you know Python, you already pretty much know how these work.

So, for example, the value of a py:if expression is false for all the same expressions where it would be false in Python. That is, py:if="x" is false if x is (1) an empty string, dictionary, or list; or (2) if x is 0, and 3 if x is the Boolean value False.

When a py:if statement evaluates to false, Kid removes the tag that it is an attribute of, along with all of its descendant elements.

In other words if the expression evaluates to anything false in Python, the tag and all its children will not be displayed.

The py:for works in much the same way, iterating over a list, and creating a new element for each value in the list. And just like py:for, any descendant elements are also reevaluated for each element. This makes it easy to turn a list of items into repeating page elements of any kind.

After you get the hang of this, it is easy to create highly involved layouts that iterate over complex data structures with a few simple Kid structures, as you saw with the WhatWhat Status projects page in Chapter 9, "Ajax and WhatWhat Status Projects."

When using py:for and py:if, however, you need to remember a couple of things. For one thing, the physical order you use for a set of py:for and py:if directives on the same tag matters. The py:for is always executed first. This is almost always what you want, because you are usually looping over a list of items and then checking some expression on each of the members of that list, to determine whether that particular list item ought to be displayed. If you really want a different execution order, just move the py:if to a <span> tag outside of the tag you will be iterating over, and you'll get what you want.

With what you know about Kid so far, you can solve every possible XML template problem, but it wouldn't necessarily be pretty. You would often have to repeat code within a page, as well as between pages. Lucky for us, Kid has a couple more processing directives that make code reuse a lot easier.

We'll get to see how to use Kid to reuse elements of another page in the next section, but let's take a look at py:def, which enables us to create reusable template functions.

When you add a py:def directive to a tag, you are creating a new function that encompasses that tag and all its member elements. Like a standard Python function, this new named template function can take any number of arguments (including *args and **kwargs style arguments).

When a tag has the py:def processing attribute, it is not rendered in the template where it appearsinstead, it is rendered when it is called from a content-producing construct somewhere later in the template.

So, for example, we could use py:def to format the output of one of our bookmark objects from Part II. To do this, all you have to do is write something like this:

<p py:def="display_bookmark{bookmark}">   The database contains a reference to ${bookmark.name} <br />   at the web location ${bookmark.link} </p>


And then you could tell Kid to display a single bookmark (passed in from the controller as current_bookmark) in our special format by calling the above template function like this:

${display_bookmark(current_bookmark)}


Or you could use the same function to display each of the bookmarks in the database like this:

<span py:for="bookmark in bookmarks">   <p py:replace="display_bookmark(bookmark)">     A list of all the bookmarks in the database will be printed here.   </p> </span>


py:def makes it possible to build large, complex XML documents based on reusable components. This isn't always critical for XHTML generation, but when you start creating larger XML documents, it's an absolutely necessary feature. And there are lots of places, even in XHTML, where you can use py:def to avoid code duplication and make your template code easier to read, understand, and maintain.

13.1.4. Kid's Built-In Functions

Before we move on to inter-template code reuse, let's take a look at the internal Kid functions that let you import XML content into your template. The first, and most often used, version is the XML() function, which allows you to pass an object containing properly formatted XML content into your template, bypassing the escaping mechanisms of Kid's standard content-producing constructs.

We saw the XML() function earlier in Chapter 8, "RSS, Cookies, and Dynamic Views in WhatWhat Status," because the WhatWhat project used Python's textile module to create XHTML markup from specially formatted strings.

But what if the XML you want isn't already inside some Python object? Perhaps it's sitting in a file on your system, or hanging out at some URL on the Internet. You could use a <?python ?> directive to get the XML from wherever it lives and package it up into a python object for you. But because this is a common need, Kid provides another shortcut function, document(). This can prove useful for all kinds of complex applications that pull XML from various places to put together a page. But you can also use it for simple things. For example, the Kid documentation provides us with this example:

<div py:content="document('header.html')"></div>


Kid's document() function resolves relative paths based on the location of the Kid template file.




Rapid Web Applications with TurboGears(c) Using Python to Create Ajax-Powered Sites
Rapid Web Applications with TurboGears: Using Python to Create Ajax-Powered Sites
ISBN: 0132433885
EAN: 2147483647
Year: 2006
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net