Python Web Templating Battle

This is my first presentation in Python conference Australia. It is held in Hobart, Tasmania on August 18 and 19, 2012. Below are the notes that I used for the presentation.

Thank you, Mark for introducing me and also thank you everyone here on interesting on this talk, Python web templating battle. This talk will be an introduction to python web templating in general.

This talk will start with an introduction for releasing my stress. After that, I will be covering five Python web templatings in brief, which will be including features, template syntax and template loader. Then, we will have 5 minutes discussion or question and answer section to end this talk.

Before I start, allowing me to spend a few seconds on introdution our current company, that I am working at, PretaWeb. The Python web templatings that we used in this company are Diazo, Extensible Stylesheet Language Transformations or XSLT and Template Attribute Language or TAL. PretaWeb specialises in the provision and support of high availability, dedicated and SaaS CMS solutions for government.

Here, I want to disclaim that, the opinions expressed in this talk, are my own personal opinions, and do not represent my employer’s view in any way.

First, I will start with the definition of web template. What is web template? It is a tool used to separate content from presentation in web design, and for mass-production of web documents. It is a basic component of a web template system. Then what is web template system? It describes the software and methodologies used to produce web pages and for deployment on websites and delivery over the Internet. Such systems process web templates, using a template engine. It is a web publishing tool present in content management systems, software frameworks, HTML editors, and many other contexts.

Web templating involves the presentation of information in a form which is often (but not always) intended to be readable, even attractive, to a human audience. Frequently, templating solutions involve a document (the template) which may look somewhat like the final output but perhaps in a simplified or stylized form, along with some data which must be presented using that template; combining these two things produces the final output which in web templating is usually (but not always) a Web page of some kind.

There are many, many different HTML/XML templating packages and modules for Python that provide different feature sets and syntaxes. These libraries usually assume that you know how to write HTML or XML. But there are also many text based templating packages in Python. Here, I are going to discuss few of them.

These are the five templating packages that I am going to discuss today:

  • Django template language is for Django web framework. It is a text based template language.
  • Chameleon, which is a HTML or XML template package, used by Zope, Plone or Pyramid web framework.
  • Jinja2 is another text based template language that similar with Django template language but provides function or macro expressions in the template.
  • Diazo, is new feature in Plone 4.2. It defines a set of rules for merging Plone content with HTML wireframe from web designer and becoming Plone theme without touching the source code. It is also a HTML or XML template package.
  • Mako is also a text based language but it allow developer to embedded Python code in the template.

Just a quick show of hands, does anyone use all of the them? Four of the them? One of them? Good. At least most of the audience use at least one of the template language.

Django Template Language

  • Text based template language
  • Define variables and basic control logic
  • Used in Django web framework
  • Custom tag and filter in Python code

Django's template language is a text based template language. It designed to strike a balance between power and ease. It's designed to feel comfortable to those used to working with HTML. The Django template system provides tags which function similarly to some programming constructs - an if tag for boolean tests, a for tag for looping, etc. But these are not simply executed as the corresponding Python code, and the template system will not execute arbitrary Python expressions. Beside tags, filters and syntax that are provided by Django, you can add your own extensions to the template language as needed.

Django - Template Syntax

A template is a text document, or a normal Python string, that is marked-up using the Django template language. A template can contain block tags or variables:

A block tag is a symbol within a template that does something. For example, a block tag can output content, serve as a control structure (an "if" statement or "for" loop), grab content from a database or enable access to other template tags. Block tags are surrounded by "{%" and "%}". Example template with block tags:

A variable is a symbol within a template that outputs a value. Variable tags are surrounded by "{{" and "}}". Example template with variables: My first name is {{ first_name }}. My last name is {{ last_name }}. A context is a "variable name" -> "variable value" mapping that is passed to a template. A template renders a context by replacing the variable "holes" with values from the context and executing all block tags.

You can modify variables for display by using filters. Filters look like this: {{ name|lower }}. This displays the value of the {{ name }} variable after being filtered through the lower filter, which converts text to lowercase. Use a pipe symbol (|) to apply a filter. Some filters take arguments. A filter argument looks like this: {{ bio|truncatewords:30 }}. This will display the first 30 words of the bio variable. Filter arguments that contain spaces must be quoted; for example, to join a list with commas and spaced you'd use {{ list|join:", " }}.

Django - Custom Filter

Django's template system comes with a wide variety of built-in tags and filters designed to address the presentation logic needs of your application. Nevertheless, you may find yourself needing functionality that is not covered by the core set of template primitives. You can extend the template engine by defining custom tags and filters using Python, and then make them available to your templates using the {% load %} tag.

Custom filters are just Python functions that take one or two arguments:

  • The value of the variable (input) - not necessarily a string.
  • The value of the argument - this can have a default value, or be left out altogether.

For example, in the filter {{ var|foo:"bar" }}, the filter foo would be passed the variable var and the argument "bar".

Filter functions should always return something. They shouldn't raise exceptions. They should fail silently. In case of error, they should return either the original input or an empty string - whichever makes more sense.

Here's an example filter definition:

And here's an example of how that filter would be used:

Django - Template loader

There are few ways to load django template. One of the them is using 'django.template.Template' class. When using this class, you compile the raw template code into a Template object first. Then call the render() method of the Template object with a given context:

If you're using Django's render_to_response() shortcut to populate a template with the contents of a dictionary, your template will be passed a Context instance by default (not a RequestContext). To use a RequestContext in your template rendering, pass an optional third argument to render_to_response(): a RequestContext instance. Your code might look like this:

Chameleon

  • HTML/XML template engine
  • Compiles into Python bytecode
  • The language used is page templates, originally a Zope invention
  • Uses Python as the default expression language
  • Embedded Python code
  • Used by Pyramid, Zope, Plone and Grok projects

Chameleon is an HTML/XML template engine for Python. It's designed to generate the document output of a web application, typically HTML markup or XML. The language used is page templates, originally a Zope invention, but available here as a standalone library that you can use in any script or application running Python 2.5 and up (including 3.x and pypy). The template engine compiles templates into Python byte-code and is optimized for speed.

The template language specifications and API for the Page Templates engine are based on Zope Page Templates, zope.pagetemplate. However, the Chameleon compiler and Page Templates engine is an entirely new codebase, packaged as a standalone distribution. It does require a Zope software environment. We can use Chameleon in Pyramid, Zope, Plone and Grok projects.

Chameleon - Template Syntax

The page templates language is used within your document structure as special element attributes and text markup. Using a set of simple language constructs, you control the document flow, element repetition, text replacement and translation.

If you've used page templates in a Zope environment previously, note that Chameleon uses Python as the default expression language (instead of path expressions). The basic language (known as the template attribute language or TAL) is simple enough to grasp from an example:

Chameleon - Attribute Language

An attribute language is a programming language designed to render documents written in XML or HTML markup. The statements of the language are document tags with special attributes, and look like this:

In the above example, the attribute namespace-prefix:command="argument" is the statement, and the entire paragraph tag is the statement's element. The statement's element is the portion of the document on which this statement operates.

Chameleon - Namespace Prefixes

The namespace prefixes are typically declared once, at the top of a template (note that prefix declarations for the template language namespaces are omitted from the template output):

Chameleon sets up defaults for tal, metal and i18n as well. Default prefixes are a special feature of Chameleon:

Chameleon - Switch and Case

The switch and case construct is a short-hand syntax for evaluating a set of expressions against a parent value. The tal:switch statement is used to set a new parent value and the tal:case statement works like a condition and only allows content if the expression matches the value. These statements are only available in Chameleon 2.x and not part of the ZPT specification:

Chameleon - Repeat

The tal:repeat statement replicates a sub-tree of your document once for each item in a sequence. The expression should evaluate to a sequence. If the sequence is empty, then the statement element is deleted, otherwise it is repeated for each value in the sequence. If the expression is default, then the element is left unchanged, and no new variables are defined. The variable_name is used to define a local variable and a repeat variable. For each repetition, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object. Both even and odd are repeat variables. Even is True when even-indexed repetitions (0, 2, 4, ...). And odd is True when odd-indexed repetitions (1, 3, 5, ...):

Chameleon - Expression Type

The default expression type is Python. The Zope reference engine defaults to a path expression type, which is closely tied to the Zope framework. This expression is not implemented in Chameleon (but it’s available in a Zope framework compatibility package). Python expressions are executed natively within the translated template source code. There is no built-in security apparatus:

Chameleon - Embedded Python Code

The](?python ... ?> notation allows you to embed Python code in templates. The scope of name assignments is up to the nearest macro definition, or the template, if macros are not used. Note that code blocks can span multiple line and start on the next line of where the processing instruction begins:

Chameleon - Comment statement

You can apply the "!" and "?" modifiers to change how comments are processed:

Chameleon - Extension

You can extend the language through the expression engine by writing your own expression compiler.

Let's try and write an expression compiler for an expression type that will simply uppercase the supplied value. We'll call it upper.

You can write such a compiler as a closure:

To make it available under a certain prefix, we'll add it to the expression types dictionary:

Alternatively, you could subclass the template class and set the attribute expression_types to a dictionary that includes your expression:

You can now uppercase strings natively in your templates:

It’s probably best to stick with a Python expression:

Chameleon - Template loader

There are several template constructor classes available, one for each of the combinations text or xml, and string or file.

The file-based constructor requires an absolute path. To set up a templates directory once, use the template loader class:

Then, to load a template relative to the provided path, use dictionary syntax:

Alternatively, use the appropriate template class directly. Let’s try with a string input:

All template instances are callable. Provide variables by keyword argument:

Jinja2

  • Function/macros
  • Text based template language
  • Compiles down to the python bytecode
  • Heavily inspired by Django and Python
  • General purpose template language

Jinga2 is a text based template language. The template syntax is heavily inspired by Django and Python. It also compiles down to the python bytecode. A template is simply a text file. It can generate any text-based format (HTML, XML, CSV, LaTeX, etc.). It doesn’t have a specific extension, .html or .xml are just fine. A template contains variables or expressions, which get replaced with values when the template is evaluated, and tags, which control the logic of the template.

Jinja2 - Template Syntax

This covers the default settings. The application developer might have changed the syntax from {% foo %} to](% foo %> or something similar:

There are two kinds of delimiters. {% ... %} and {{ ... }}. The first one is used to execute statements such as for-loops or assign values, the latter prints the result of the expression to the template.

Jinja2 - Variables

The application passes variables to the templates you can mess around in the template. Variables may have attributes or elements on them you can access too:

Jinja2 - Filters

Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (|) and may have optional arguments in parentheses. Multiple filters can be chained. The output of one filter is applied to the next. {{ name|striptags|title }} for example will remove all HTML Tags from the name and title-cases it. Filters that accept arguments have parentheses around the arguments, like a function call. This example will join a list by commas: {{ list|join(’,’) }}:

Jinja2 - Comments

To comment-out part of a line in a template, use the comment syntax which is by default set to {# ... #}. This is useful to comment out parts of the template for debugging or to add information for other template designers or yourself:

Jinja2 - Control Structures

A control structure refers to all those things that control the flow of a program - conditionals (i.e. if/elif/else), for-loops, as well as things like macros and blocks. Control structures appear inside {% ... %} blocks in the default syntax. For example, for, it loops over each item in a sequence:

Jinja2 - Call

Macros are comparable with functions in regular programming languages. They are useful to put often used idioms into reusable functions to not repeat yourself. In some cases, it can be useful to pass a macro to another macro. For this purpose, you can use the special call block. The following example shows a macro that takes advantage of the call functionality and how it can be used:

Jinja2 - Template Loader

Jinja2 uses a central object called the template Environment. Instances of this class are used to store the configuration, global objects and are used to load templates from the file system or other locations. The simplest way to configure Jinja2 to load templates for your application looks roughly like this:

This will create a template environment with the default settings and a loader that looks up the templates in the templates folder inside the 'yourapplication' python package. To load a template from this environment you just have to call the get_template() method which then returns the loaded Template. To render it with some variables, just call the render() method.

Diazo and XSLT

Diazo allows you to apply a theme contained in a static HTML web page to a dynamic website created using any server-side technology. With Diazo, you can take an HTML wireframe created by a web designer and turn it into a theme for your favourite CMS, redesign the user interface of a legacy web application without even having access to the original source code, or build a unified user experience across multiple disparate systems, all in a matter of hours, not weeks. When using Diazo, you will work with syntax and concepts familiar from working with HTML and CSS. And by allowing you seamlessly integrate XSLT into your rule files, Diazo makes common cases simple and complex requirements possible:

Diazo - Template Syntax

Inside the diazo folder, you will need at least two files, theme.xml and rules.html.

The theme.html is a static HTML design from web designer. It might come with placeholder content and images, stylesheets and JavaScript resources included via relative links. You would normally be able to test the theme by opening it with web browser:

The rules.xml is Diazo rule file. It contains the Diazo directives that merge the content (the thing we are applying the theme to) into the theme, replacing placeholders with real content. I will explain later what are drop and replace directive statements mean:

The rules file contains an XML document that is is rooted in a tag called](rules />. Here we have defined three namespaces: the default namespace is used for rules and XPath selectors. The css namespace is used for CSS3 selectors. These are functionally equivalent to the XPath selectors. In fact, CSS selectors are replaced by the equivalent XPath selector during the pre-processing step of the compiler. Thus, they have no performance impact. The xsl namespace is used if you want to add inline XSLT directives for fine-grained control.

Diazo - Directives

The following directives are allowed inside the](rules /> element in the rules file:

Here, we will be discussing three directives, theme, drop and replace.

The](theme /> is used to specify the theme file.

The](replace /> is used to replace an element in the theme entirely with an element in the content.

The](drop /> is used to drop elements from the theme or the content. This is the only element that accepts either theme or content attributes (or their css: and -children equivalents), but not both.

Diazo - Attributes

theme or theme-children or css:theme or css:theme-children - Used to specify the node(s) in the theme that is to be replaced. When using theme-children, all elements inside the tag that matches the XPath or CSS expression will be replaced, but the matched tag itself will remain intact.

content or content-children or css:content or css:content-children - Used to specify the node in the content that is to replace the matched node(s) in the theme. When using content-children, all elements inside the tag that matches the XPath or CSS expression will be used, but the matched tag itself will be left out.

Diazo - Conditional Rules

Sometimes, it is useful to apply a rule only if a given element appears or does not appear in the markup. The if, if-content and if-path attributes can be used with any rule, as well as the](theme /> and](notheme /> directives. if-content should be set to an XPath expression. You can also use css:if-content with a CSS3 expression. If the expression matches a node in the content, the rule will be applied.

Diazo - XSLT

There is no loop logic in Diazo? Wait!

Here come the mix of XSLT with Diazo. It is possible to insert XSLT instructions into the compiled theme in this manner. This code is looping](li> in](ul id=”details”> of the content. And then copy anchor element () in definition term element (

) and image element () in definition description element (
).

Diazo - Compilation

Once you have written your rules file, you need to compile it to an XSLT for deployment. In some cases, you may have an application server that does this on the fly, e.g. if you are using the plone.app.theming package with Plone. For deployment to a web server like Apache or Nginx, however, you will need to perform this step manually.

The easiest way to invoke the Diazo compiler is via the diazocompiler command line script which is installed with the diazo egg.

This will print the compiled XSLT file to the standard output. You can save it to a file instead using:

Diazo - Test Run

To test the compiled theme, you can apply it to a static file representing the content. The easiest way to do this is via the diazorun script:

Diazo - Template Loader

We can use Diazo in few ways. One of them is in Plone. When using in Plone, we have to include plone.app.theming package in Plone buildout configuration file. Then register Diazo folder in configure.zcml file.

Mako

  • is an embedded Python (i.e. Python Server Page) language
  • text based template language
  • compiles down to the python bytecode
  • function/macros
  • control structures constructed from real Python code (i.e. loops, conditionals)
  • straight Python blocks, inline or at the module-level

Mako - Template Syntax

Mako - Control Structures

A control structure refers to all those things that control the flow of a program – conditionals (i.e. if/else), loops (like while and for), as well as things like try/except. In Mako, control structures are written using the % marker followed by a regular Python control expression, and are “closed” by using another % marker with the tag “end”, where “” is the keyword of the expression

Mako - Def Tag

The %def tag defines a Python function which contains a set of content, that can be called at some other point in the template. The %def tag is a lot more powerful than a plain Python def, as the Mako compiler provides many extra services with %def that you wouldn’t normally have, such as the ability to export defs as template “methods”, automatic propagation of the current Context, buffering / filtering / caching flags, and def calls with content, which enable packages of defs to be sent as arguments to other def calls (not as hard as it sounds).

Mako - Inherit Tag

Inherit allows templates to arrange themselves in inheritance chains. This is a concept familiar in many other template languages. When using the %inherit tag, control is passed to the topmost inherited template first, which then decides how to handle calling areas of content from its inheriting templates. Mako offers a lot of flexibility in this area, including dynamic inheritance, content wrapping, and polymorphic method calls.

Mako - Python Blocks

Any arbitrary block of python can be dropped in using the](% %> tags. Within](% %>, you’re writing a regular block of Python code. While the code can appear with an arbitrary level of preceding whitespace, it has to be consistently formatted with itself. Mako’s compiler will adjust the block of Python to be consistent with the surrounding generated Python code.

Mako - Module-level Blocks

A variant on](% %> is the module-level code block, denoted by](%! %>. Code within these tags is executed at the module level of the template, and not within the rendering function of the template. Therefore, this code does not have access to the template’s context and is only executed when the template is loaded into memory (which can be only once per application, or more, depending on the runtime environment). Use the](%! %> tags to declare your template’s imports, as well as any pure-Python functions you might want to declare:

Any number of](%! %> blocks can be declared anywhere in a template; they will be rendered in the resulting module in a single contiguous block above all render callables, in the order in which they appear in the source template.

Mako - Template Loader

A Template can also load its template source code from a file, using the filename keyword argument:

For improved performance, a Template which is loaded from a file can also cache the source code to its generated module on the filesystem as a regular Python module file (i.e. a .py file). To do this, just add the module_directory argument to the template:

When the above code is rendered, a file /tmp/mako_modules/docs/mytmpl.txt.py is created containing the source code for the module. The next time a Template with the same arguments is created, this module file will be automatically re-used.

Summary

I would like to apologize that the title is a bit misleading. There is no battle in the round that one of web template will win. Everyone is a winner I believe every single web templates have their own place in this market. They have their own unique feature that stand out from the rest. Some influence from other templates. Everyone learn from each other. After all, all is open source. Do you think they should combine into one standard Python web templating? We can see there are lots of similarity of each of them. Some web frameworks are supporting to have more than one web templating?

Discussion / Question and Answer

Any questions from the floor? Do you guys have any bad experience on any of the web templating to share?

Thank You

Thank you everyone that are coming to this talk.

Video

Python Web Templating Battle

Python Web Templating Battle by PyCon Australia

Reference

Comments

Comments powered by Disqus