Summary
I finally got some time to play with Cheetah and Django templates. After maybe an hour with each, I like Django best. Hopefully people are still watching this space...
Advertisement
Encouraged by Fredrik Lundh, I took a look at Django templating, separate from the rest of Django. This works fine, except there's a mysterious environment variable DJANGO_SETTINGS_MODULE that is somehow required. That may be fine for framework users, but it's a bit harsh if all you want to do is use the templating library. Setting it to empty didn't help. I ended up uing this hack:
import os
os.environ["DJANGO_SETTINGS_MODULE"] = "__main__"
from django.core.template import Template
With that out of the way, I managed to run this program:
t = Template("<h1>Hello {{name}}</h1>")
print t.render({"name": "Phillip"})
print t.render({"name": "Adrian"})
This is nice! The output of course is:
<h1>Hello Phillip</h1>
<h1>Hello Adrian</h1>
Unfortunately I had trouble installing Django for the Python 2.5 alpha that I've got installed; it uses the fancy new ez_install stuff, which is great when it works, but there's no Python 2.5 download of setuptools on PyPI yet, so it doesn't.
I tried the same exercise using Cheetah. Not too different:
from Cheetah.Template import Template
names = {"name": "Tavis"}
t = Template("<h1>Hello $name</h1>", searchList=[names])
print t
names["name"] = "Ian"
print t
with the following output:
<h1>Hello Tavis</h1>
<h1>Hello Ian</h1>
(Ironically, Cheetah also failed under Python 2.5 -- Python 2.5 inadvertently (I believe) changes the syntax that's allowed before "from __future__ import ..." to disallow assignment to __author__ and __version__. That was easier to fix though -- commenting out the "from __future__ ..." line was sufficient.)
(BEWARE! I haven't used either system to build a real website yet. I'm just looking at the API and templating language designs.)
Django definitely feels more "modern" than Cheetah. The templating languages are fairly similar, with Django writing {{foo.bar}} where Cheetah writes $foo.bar or ${foo.bar} for variable interpolation (== substitution). The biggest difference is that Cheetah allows pretty much arbitrary Python call syntax, e.g. ${foo.bar('hello', $name, 42+42)}. Yes, you have to prefix variable references in the argument list with another $, and there are confusing rules about when the $ is optional. Django only allows names separated by dots, using the old Zope trick of trying x['y'], x.y and x.y() when the input is {{x.y}}. When y looks like a number, it'll even try a sequence index, e.g. {{x.4}} means x[4].
(Aside: something similar is also found in web.py, but there it bothers me, because it's invoked from normal-looking Python code. E.g. foo["x"] and foo.x are equivalent, when foo is a "Storage" object. But this leaves me wondering, what does foo.keys mean? The keys method or the value stored under "keys"? Zope 2 was similarly confusing with implicit acquisition.)
A big difference: if Django doesn't find a name, it inserts nothing; but Cheetah raises an exception. ISTM that Django is more user-friendly here, even if its approach could be considered error-prone (typos are easily missed since they simply suppress a small bit of output). In my experience, missing variables are very common in substitution data, and Cheetah requires you to provide explicit default values in this case.
Both templating languages also have a "statement" syntax to complement their "expression" interpolating syntax. In Django, this is written as {% keyword %} while in Cheetah you use #keyword. Here I initially found Cheetah a bit more readable, since it resembles C preprocessor syntax:
#if $name
<h1>Hello $name</h1>
#else
<h1.Hello there</h1>
#end if
which is harder on the eyes and prints more unnecessary whitespace.
But then I stumbled upon Cheeta's inline form, which is pretty unreadable due to the symmetric delimiters:
# if $name # <h1>Hello $name</h1> # else # <h1>Hello There</h1> # end if #
Both languages have tons of other statement-level constructs, to do loops, set variables, invoke other templates, define blocks that can be used by other templates, and more. I haven't explored this much yet, but they both seem to cover a similar terrain. I guess this is what template authors actually need; or perhaps it points to some common ancestor in PHP or JSP?
If you need your variable interpolations to be HTML-escaped (replacing "<" with "<" and so on), Cheetah lets you specify a default filter in the template (#filter) or when the template is created in Python code. In Django you must add a pipeline to the interpolation syntax, like this: ${foo.bar|escape}. Both support various other filters as well, with Django really going wild. I'm somehow surprised that HTML-escaping isn't the default -- failing to HTML-escape data is the number one vulnerability leading to XSS attacks. And in theory you should almost never need to provide HTML for interpolation: you're supposed to invoke HTML fragments using #include or {%include%}. Cheetah at least provides a way to make HTML-escaping the default filter throughout a template.
See the examples near the top. I like Django's version better: you pass the variable bindings in to the render() method. Cheetah lets you specify multiple dictionaries with variable bindings, which are searched one after another, but it bothers me that these all have to be passed to the Template() constructor instead of to the render method (which is called __str__() in Cheetah :-).
Django's template compilation is much simpler and IMO more elegant than Cheetah: Django parses the template text into nodes of various types using a big regular expression, and each node has an appropriate render() method. Rendering the template in a given context simply concatenates the results of rendering each node in that context. I imagine this could easily be turned into a generator compatible with WSGI.
Cheetah, OTOH, compiles each template to a Python class! This is much slower, and in my experience brittle -- on my first attempt I introduced a syntax error in the template that caused a Python syntax error in the resulting Python class, which was hard to debug. It also seems overkill, and I worry that it might cause security problems -- given that the compiler isn't too smart (see above) I could see a malicious template author "breaking out" of the templating languages and invoking unauthorized Python code. Now, I wouldn't let people I don't trust edit templates on my website anyway, but it appears to be a common pattern that certain people are allowed to edit templates but not code. Cheetah blurs the distinction a little too much for my comfort.
I guess we're closer than I thought to the mix-and-match approach that I requested in my previous blog. Cheetah is just a templating engine, and several web frameworks use or recommend it, e.g. web.py and Subway. (Is Subway dead? The website is super-incomplete and it still downloads an old Cheetah version.) Django's templates are almost usable independent from the rest of Django; I expect the situation will improve once they release the magic-removal branch.
I didn't find too many other templating solutions. TurboGears uses Kid which XML-based, like Nevow, TAL etc. IMO these are no contenders because they are XML-based. As the Django folks mention, they use templates to generate non-HTML text files as well. And even if they could be used for this, the verbosity of the XML syntax and the inability to do a proper if-then-else in XML make template writing using XML a non-starter.
Django and Cheetah both define a 'Template' class. Python's standard library also has a Template class (in string.py), which serves a similar purpose (but with only a fraction of the functionality). All these have different APIs. But are the differences important? It seems pretty arbitrary whether to use Template("...").render(locals()) (Django), or str(Template("...",searchList=[locals()]))`(Cheetah)or``Template("...").substitute(locals()) (string.py). Perhaps we could attempt some standardization here similar to WSGI?
I asked on python-dev about the __future__ business. It appears that what Cheetah is doing is actually illegal; apart from comments and a doc string, *nothing* may precede a future statement. That this isn't enforced in Python 2.2 - 2.4 is a bug.
I've been pretty happy with Django's overall architecture and loose coupling. The DJANGO_SETTINGS_MODULE thing is indeed one of the small flaws, but its used to create seperate "global settings" files. You'll need to create a settings.py file and give its dotted python address to that environment var to set up things like the automatic TEMPLATE_LOADERS (such as the file system loader and its search paths).
> I'm somehow surprised that HTML-escaping isn't the default > -- failing to HTML-escape data is the number one > vulnerability leading to XSS attacks. And in theory you > should almost never need to provide HTML for interpolation: > you're supposed to invoke HTML fragments using #include or > {%include%}. Cheetah at least provides a way to make > HTML-escaping the default filter throughout a template.
I don't think it makes that much sense to auto-HTML-escape, as this reeks of the PHP magic quotes problem. The Template language is use for non-HTML writing, and some of use have preferences for things like reStructuredText instead of letting users input arbitrary HTML subsets.
There isn't a default filter (and I would be hesitant to use such a thing), but you can have "block filters" if you are using it across a lot of nearby variables...
Thank you Guido for honest remarks on both templating engines. Like I assumed an actual use is the best way to dissect features and understand concepts.
> Cheetah, OTOH, compiles each template to a Python > class! This is much slower, and in my experience brittle > -- on my first attempt I introduced a syntax error in the > template that caused a Python syntax error in the > resulting Python class, which was hard to debug. It also
AFAIK original Django authors decided against using templates as a front for Python interpreter precisely because of this reason: templates should not be able to bring down the system no matter what. They should provide a functionality required by web designers and nothing more. Otherwise it is a huge liability, a potential security hole, and a maintenance nightmare.
> I didn't find too many other templating solutions. > TurboGears uses Kid which XML-based, like Nevow, TAL etc. > IMO these are no contenders because they are > XML-based. As the Django folks mention, they use > templates to generate non-HTML text files as well. And
AFAIK that was another major design decision: the ability to generate non-XML content. It makes possible to generate e-mail texts, custom CSS files, data-driven JavaScript stubs, and so on without using home-brewed or 3rd-party solutions.
> Perhaps we could attempt some standardization here > similar to WSGI?
It looks like common API to a template engine is quite possible. One potential problem is what kind of data are acceptable as parameters, because it will dictate what should be understood by the engine. E.g., if I can pass a list, it is logical to expect that the engine provides a set of list-specific operations, and so on.
The one thing I dislike about both template systems is that they're not XML -- you can build invalid output with them if you mess up, which is Bad.
I've switched to Kid. Problem gone, and my templates are editable with "real" XML editors and survive mangling by programs (or even XSLT) that don't know zilch about the template system.
Thanks Guido. I have been following the related posts on web frameworks. Having been with drupal.org (coded in php) for more than a year, i was looking for frameworks in python. Since this topic is now fuelled by none other the python creator, i am hopeful that we can look forward for emphasis on standard web development framework coded in the beautiful language "python".
Bah, I completely missed the paragraph with Guido's "kid" comment. :-/
My opinion, slightly longer:
Kid's XML is sufficiently non-verbose that I dont see any difference in writing effort. Even better, if you use an XML editor you can put the conditionals etc. right into your element templates.
If you really need more complex if/then/else logic than "if X" vs. "if not X" then that decision seems like it is somewhat misplaced in the templating system.
Output which is not XML can easily be produced by actual Python code. Kid already has a plain-text filter if you do need a template as a front-end to that, and adding others is easy. In fact, TurboGears standard output is normal-looking HTML4, not xhtml, unless your browser specifically asks for it.
The point is that with Kid the template output is inherently machine parse- and -postprocessable; Kid translates the code to a Python function which yields an xml element stream, not a random-text stream. IMHO that's an advantage for any more complex system.
"And in theory you should almost never need to provide HTML for interpolation: you're supposed to invoke HTML fragments using #include or {%include%}."
That depends on how you're producing your content, of course. I'm doing a lot of "document" work, rather than "data entry" work, and I keep ending up with a pattern where my core application produces XHTML fragments, which are combined in different ways by the template layer. My little Django hack is a typical example: the underlying system produces XHTML infosets (as ET structures), which are serialized to HTML fragments, and combined with HTML boilerplate by the template layer.
This is perhaps the main difference between Kid and Cheetah/Django-style templating: Kid focusses on the infoset (you're describing the the DOM tree in the web browser). If your problem is infoset related, a Kid/PyMeld3/4-style solution (or just raw ET) can be a lot easier to use. If your problem is more textual, a text-oriented templating system is easier to use.
For me, a mix is what works best in most cases; use infoset tools for the information content, and textual tools for boilerplate and styling.
I personally still prefer templating that doesn't involve *any* XML markup, Stan and XIST are the top two contenders in my mind, although there are others. Stan is a little odd as it has *no* control structures: it forces you to put your logic back in the Python controller (arguably where it belongs). This is a bit straining at first, but after a while you come to appreciate it, and Stan provides appropriate hooks for doing so in a nice way.
Stan is by far the nicest DSL I've seen for XML generation in any language. The mapping from Stan to XML is quite direct and easy for even designers to comprehend. It's also easy to create new tags (usually one line of code suffices)
XIST looks pretty nice too, but having settled on Stan I haven't explored it very much beyond the online documentation.
I've spend quite a lot of time looking at the various templating systems for python. They are easy to write so there is a lot of them. If you are interested I have a full report writen on all of those. I guess I should start a blog...
Anyway, find the list below. After a long investigation my favorite is cubictemp. Simple. Powerful. Well documented.
Making good looking HTML is hard enough without mixing it with a programming language. In addition, you are generating the HTML out of context. Its much better to use a nice wysiwyg designt tool for the HTML, and keep the code separate. Simply put it is necessary to separate the graphic design and the functionality.
I have been using various techniques to do this for a number of years now, and its getting easier all the time due to browser improvements.
Take a look at what is now called AJAX (New name for a very old dog!). This allows you to treat a web page as a data source, and then fill the called function results back into the web page without refreshing the HTML. This means that the style information is not messed with, just the data. The only drawback is that you may have to learn javascript if you dont find the libs you need on the web.
Development is then a whizz... The graphic designer designs the HTML, the programmer then supplies some standard javascript libs and the server side is just data interchange. I find this approach works well (at least for me).
It's possible to use Quixote's htmltext type with Cheetah; Mike Orr wrote a Linux Gazette article about it (see http://linuxgazette.net/117/orr.html for details). I'm using this approach for my web application, and it's really convenient for ensuring that data always gets escaped, but gets escaped exactly once.
Anyone know if it's possible to use htmltext with Django? Google doesn't find any examples of someone doing it.
Flat View: This topic has 86 replies
on 6 pages
[
123456
|
»
]