Python templates – Django and Cheetah
When writing web applications, sooner or later (usually sooner),
everybody is going to need a template
language. String-interpolation just doesn't cut it. We
need a way to write something that is almost all text
(or HTML, or XML, or whatever), but with some dynamic pieces
thrown in.
Since this need is so universal, and the basic requirements are
so easy to describe, many different groups of people have taken
it upon themselves to create Yet Another Template Language. As
developers, we can join the fray and roll our own, or we can
wade through the myriad options available to us to find the one
that meets our needs or philosophy. Those who use PHP or ASP
pretty much have the choice made for them, since the languages
themselves are glorified template processors. Python
programmers have a lot more options.
Here I'm just going to focus on the two Python templating
languages I have used in real applications: Cheetah and the
Django templating engine. (Django, of course, is more than
just templates, but the template subsystem can be used
independently.) I use and enjoy both of these, but there are
significant differences that are worth comparing and
contrasting, when deciding which to use for your particular
needs. There are other comparisons out there, including one by
the Benevolent Dictator for Life himself (though that's a bit
out of date and inaccurate). When choosing which to use, you
should read as many opinions as you can, then make the decision
yourself. Presented here are just my personal thoughts.
(more...)
Let the Limericks Flow
We've created an online contest
To see who can rhyme the bestest
So come to our site
And prove you can write
The limerick that rates the highest.
Sorry, that's what happens when you try to write a limerick after four hours of reading technical specification documents. If you think you can do better, come to our limerick contest website and make an entry, read and rate other entries, and waste some time.
For the developers, the contest site is hosted on Django with a MySQL backend.
There will be a new category of limerick writing contest every month. This month's theme is cheese. Enjoy!
More on editing Django templates in XEmacs
Previously, I wrote about how I set up syntax-highlighting for
Django template files using MMM (multiple major modes) in
XEmacs. This entry builds on the previous one, so if you haven't
read that one, I suggest doing so now.
This post addresses the age old question, "If (X)Emacs is so
good, why can't it do the typing for me?" Well, the answer, of
course, is it can. You just have to write a few lines of Lisp
first. Or you can copy and paste the Lisp code off of sites like
this one. ;)
Introducing dynamic abbreviations
Pressing M-/, (that is, holding down the Alt key and
pressing the forward slash key), runs the command
dabbrev-expand, which tries to finish whatever word you
are in the middle of typing. For example, if as I'm typing this
entry, I type the letter d, then press M-/,
"dabbrev-expand" shows up, because that was the last word I
typed that started with d. If I then type M-/
again, "dabbrev-expand" changes to "down". If I keep pressing
it, the word cycles through different guesses for what I'm going
for. If you've ever used VisualStudio, you may see some
similarities to the Intellisense feature. I find I almost never
type a whole word anymore, I've become so dependent on dynamic
completion.
Templates and more abbreviations
dabbrev-expand is useful to avoid having to type long
words over and over, but there are also longer patterns that
seem to need typing frequently. Thats where the tempo
package comes in handy, especially when combined with the
abbrev library. Here is some documentation for
tempo. Here is some documentation for abbrev.
Essentially, tempo allows you to specify "templates," or
blocks of standard text that can be parameterized as they are
filled in: the same concept as Django templates, but meant for
interactive use. From now on, I will say "tempo template" or
"Django template" to avoid confusing the two types.
abbrev lets you define your own pre-set abbreviations,
which can be filled in automatically as you type or upon
request. This can be combined with tempo to do some
pretty powerful stuff very easily.
I have a few Django-specific tempo templates and abbrevs set up
in my XEmacs initialization file. For example, as soon as I type
"{% block" followed by a space, the entire framework of a Django
template block is filled in for me. I've also added a special
menu to the menu bar for tempo templates I use frequently.
HTML and templates can be very repetitive, so I've found this
saves me a lot of typing.
My initialization file
Here is the subset of my ~/.xemacs/init.el file. This includes
the mmm-mode stuff I discussed last time, as well as the
tempo templates and abbrevs. I haven't put a tempo template for
every possible Django template tag, just the ones I use most
frequently. I'll leave further extension as an exercise for the
reader.
Also, some people don't like the expansion to happen
automatically as they are typing. To turn this off, comment out
the line that says "(setq abbrev-mode t)" by putting a semicolon
() in front of it. Then you can
manually expand tempo templates by pressing C-\
(control-backslash).
Once again, this is known to work in XEmacs version 21.4 on
Windows XP. It will most likely work in other versions of
XEmacs, and possibly in GNU Emacs.
(autoload 'css-mode "css-mode" "Mode for editing CSS files" t)
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-mode))
(setq cssm-indent-function #'cssm-c-style-indenter)
(setq cssm-indent-level '2)
(add-to-list 'auto-mode-alist '("\\.tmpl\\'" . hm--html-mode))
(require 'mmm-vars)
(require 'mmm-mode)
(require 'mmm-sample)
(setq mmm-global-mode 'maybe)
(mmm-add-classes
'((my-django-expr
:submode python-mode
:face mmm-declaration-submode-face
:front "{%"
:back "%}"
:include-front t
:include-back t)))
(mmm-add-classes
'((my-django-var
:submode python
:face mmm-output-submode-face
:front "{{"
:back "}}"
:include-front t
:include-back t)))
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'embedded-css)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-var)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-expr)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'html-js)
(setq mmm-submode-decoration-level 2)
(set-face-background 'mmm-code-submode-face "#EEEEFF")
(global-set-key "\C-\\" 'tempo-complete-tag)
(require 'adapt) (require 'hm--html-mode) (require 'tempo)
(tempo-define-template "span"
'("<span class=\"" (p "Class? ") "\">" r "</span>" >)
"<span "
"Insert a template for a span with a class")
(define-abbrev hm--html-mode-abbrev-table
"<span" "" 'tempo-template-span)
(tempo-define-template "script"
'(> "<script type=\"text/javascript\">" r "</script>" > %)
"<script "
"")
(define-abbrev hm--html-mode-abbrev-table
"<script" "" 'tempo-template-script)
(tempo-define-template "block"
'("{% block " (p "Block name? ") " %}" p "{% endblock %}")
"{% block "
"")
(define-abbrev hm--html-mode-abbrev-table
"{% block" "" 'tempo-template-block)
(tempo-define-template "django-if"
'("{% if " (p "Conditional? ") " %}" p "{% endif %}")
"{% if "
"")
(define-abbrev hm--html-mode-abbrev-table
"{% if" "" 'tempo-template-django-if)
(tempo-define-template "django-for"
'("{% for "
(p "Variable? ") " in "
(p "List? ") " %}" p
"{% endfor %}")
"{% for "
"")
(define-abbrev hm--html-mode-abbrev-table
"{% for" "" 'tempo-template-django-for)
(defun add-templates-menu ()
(interactive)
(add-submenu nil '("Tem%_plates"
["{%% %_block %%}" tempo-template-block]
["{%% %_if %%}" tempo-template-django-if]
["{%% %_for %%}" tempo-template-django-for]
["<%_span>" tempo-template-span]
["<s%_cript>" tempo-template-script])))
(add-hook 'hm--html-mode-hook
(lambda ()
(setq abbrev-mode t)
(add-templates-menu)))
How I edit Django templates
NOTE: This post is pretty old, and I no longer use mmm-mode for Django template editing.
This post and this post are still accurate, though.
Every programmer has their favorite editor and mode of
working. Some people have more than one. For example, I use
Microsoft Visual Studio when editing .NET code, DrScheme for editing
Scheme code, and XEmacs for pretty much everything else.
This post is about how I use XEmacs for editing Django template
files, in the hopes that others may find this useful.
The "Other" One True Editor
It's rare that people will get passionate about something as
pedestrian as a way of editing plain text, but the brief history
of the internet is awash with flamewars and heated discussions
with titles like "Emacs vs. XEmacs," "Emacs vs vi," and so
on. I'm not about to go into the relative merits, but the fact
that certain editors pop up time and time again in these debates
must mean there is something special about them.
Early on in my college education I started using Emacs because
it was all that was available on the school's servers. (Well,
that or vi, but vi was and still is black-magic to me.) I got
over the learning curve, and now I'm hooked.
At some point, I switched from Emacs to XEmacs, for reasons I
can't remember. At the time, it had some feature which was to me
essential. That reason no longer applies, but neither have I had
a reason to switch back. These tips may apply even if you use
GNU Emacs, but they've only been tested in XEmacs.
My current setup is Xemacs 21.4 (patch 20), running on Windows
XP. *gasp!* Yes, I use Windows for Django development. Shocking,
I know. I have my reasons. In any case, these tips should work
equally well with XEmacs on other platforms.
Multiple Major Modes
I won't include a full XEmacs tutorial here, since there is
already plenty of info on the web about it. The key point
is that there is a "major mode" for each programming
environment. There is a Python mode, a Java mode, and so
on. Django templates tend to combine more than one language in a
file, so that's when the mmm library comes in handy. It
stands for "multiple major modes," and it turns out to be just
exactly what we need. We can have html-mode for the HTML parts,
JavaScript-mode for the JavaScript parts, css-mode for embedded
CSS, and python-mode for the Django template filters and tags.
Which HTML mode?
As often happens in the world of Free Software, there are
several options to choose from when setting up HTML editing in
XEmacs.
- html-mode. This has the fullest support for HTML
parsing and validation. The problem is, when dealing with
templates, the HTMl will often not validate, so all kinds of
error messages show up.
-
sgml-mode. This is a more general mode for SGML (of
which XML and HTML 4 are subsets).
-
xml-mode. SGML mode specialized for XML.
-
hm--html-mode. I have no idea what the HM stands for,
but this is a lightweight HTML mode, with basic syntax
highlighting and indentation.
I use html-mode for full-fledged HTML documents, and
hm--html-mode for templates. So that XEMacs can tell the difference,
I use the suffix ".tmpl" for template files.
One problem
I did run into some problems getting mmm-mode to work with
XEmacs. It turns out the version of mmm-mode that is distributed
with the XEmacs package system is ancient - from 2001. I had to
download the newer version of mmm-mode and unzip it into my site-packages directory.
How it looks
Here's a screenshot of me editing an example Django template
(borrowed from my beta-registration Django app). I've chosen
fairly bright colors to make the syntax highlighting more
obvious, but that's all customizable. Notice how the Django tags
and variables are easy to find in the file. (Click on the image
for a larger size.)
My initialization file
Here is the subset of my ~/.xemacs/init.el file dealing with
setting up mmm-mode for XEmacs. I hope someone finds this
useful. Let me know if you do, or if you encounter problems.
(autoload 'css-mode "css-mode" "Mode for editing CSS files" t)
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-mode))
(setq cssm-indent-function #'cssm-c-style-indenter)
(setq cssm-indent-level '2)
(add-to-list 'auto-mode-alist '("\\.tmpl\\'" . hm--html-mode))
(require 'mmm-vars)
(require 'mmm-mode)
(require 'mmm-sample)
(setq mmm-global-mode 'maybe)
(mmm-add-classes
'((my-django-expr
:submode python-mode
:face mmm-declaration-submode-face
:front "{%"
:back "%}"
:include-front t
:include-back t)))
(mmm-add-classes
'((my-django-var
:submode python
:face mmm-output-submode-face
:front "{{"
:back "}}"
:include-front t
:include-back t)))
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'embedded-css)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-var)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'my-django-expr)
(mmm-add-mode-ext-class nil "\\.tmpl\\'" 'html-js)
(setq mmm-submode-decoration-level 2)
(set-face-background 'mmm-code-submode-face "#EEEEFF")
Programming for Not-so-dummies
Somebody posted a reply to my last post about Django's autoescaping mechanism. (They were too cowardly to post on my site, so they posted it at reddit.com.) The person said something like, "you shouldn't trust yourself to remember to escape your own variables." Oh, heaven forbid I trust myself to be a good programmer! That really got me thinking about the recent trends towards designing frameworks, APIs, even languages for mediocre programmers. We are sacrificing speed, simplicity and efficiency to make common bugs less common, trying to design away the mistakes inexpensive and poorly-trained computer scientists make.
Now, of course when I say "recent trends," I should acknowledge that this type of thinking has been around for decades. It was first truly popularized with the introduction of Java. Some people forget to free memory, so add garbage collection. Some people forget to bounds-check arrays, so make that automatic. Ooh, pointers are scary! Let's get rid of them. We can't allow our outsourced foreign coders direct access to memory!
Good training, along with working for nearly a decade as an embedded software engineer, has taught me good programming habits. I've learned to be conscious of memory leaks, to always check return values, to program defensively, to bounds-check. I've created software for shipping products in such low-level and "unprotected" languages as C++, C, and even Assembly. I've written production code within less than the memory space required for a Java byte-code interpreter. And of course, I'm not alone in this. There is a large subset of software developers who had to learn to program carefully, due to constraints out of their control. These types of good programming habits carry over into whatever platform or language is used.
I feel a lot of the new safety-net style approaches are simply enabling poor programmers to work on increasingly sophisticated projects. To get back to the example from my last post, Django is a wonderful tool. You can program a sophisticated database-centered multi-user web application without even knowing how to spell SQL.
Django's recent addition of autoescaping, and more importantly, the enabling of autoescaping globally by default, is yet another example of API-design for the lowest common denominator. (I should note that I love Django. It saves me writing a lot of redundant code and provides a lot of things for free that I would otherwise need to write from scratch, so I don't mean to pick on Django here. It just happened to be the catalyst for this discussion.)
It's not all bad
I know I'm starting to sound like an old curmudgeon. "In my day, we didn't have variables, we just had to carry around rocks to count!" I'm not that old, really. And I'm certainly not advocating we go back to the days before garbage collection and bounds checking. Especially given the potential security ramifications of memory-management bugs, these things are especially important. I just want to urge caution before binding developers in a straitjacket. Rather than trying to design away all potential bugs at the level of the language or API, emphasize and facilitate good programming and testing practices. I've never once bought a For Dummies book, and I never will. Please don't force me to use a For Dummies application framework.
Escaping autoescape in Django
I've been pleased with the Django web-application platform. Programming in Python is fun and fast, and Django provides many things for free that would be a lot of work to program from scratch.
I've also enjoyed developing with the "bleeding edge" development version of Django. I like being able to use the latest features before they make it into the official releases. Whenever I stumble across what I think is a bug in Django, the first thing I do is "svn update" in my Subversion checkout, and most of the time the bug has already been fixed in the trunk.
Recently, however, a major change in the Django development version has
caused all of my projects to stop working! Needless to say, this was a bit frustrating. The change was the addition of "autoescaping" in Django.
What it is
The autoescape setting, referred to in the
Django documentation as
Automatic HTML escaping, means that any variable inserted into a rendered template gets the function django.utils.html.escape called on it. You can see what this function does in the file trunk/django/utils/html.py, but essentially as of today's code base it applies the following set of substitutions:
your_string.replace('&', '&').replace('<', '<'). \
replace('>', '>').replace('"', '').replace("'", '''))
(Ironically, I had a lot of trouble getting that code fragment to look right, due to WordPress's own autoescaping, which I ended up disabling altogether.
Aaarrgh!)
On the surface, this seems like a useful feature. It seems to have been done to "idiot-proof" the template language, and to prevent cross-site-scripting vulnerabilities in case there is user-generated text stored in variables and the programmer forgets to call the appropriate escape function.
The problems
In general, I hate this kind of stuff. I can't stand it when Microsoft Word capitalizes the first letter of my sentences. If I wanted a capital letter, I would have held down the shift key! I hate it when the rear defroster in my car shuts off automatically after 30 minutes. Hello! Just because 30 minutes have passed doesn't mean it's not still raining out; doesn't mean I don't still live in New England! Essentially, I don't like it when machines think they are smarter than I am, or when they try to do what I
mean, rather than what I
say.
If I had wanted to escape my variables, I would have escaped them. That's the first problem. This would not be such a big issue if it weren't for the second problem: this new disruptive feature is turned on by default, with no easy way to disable it across the board. I have a lot of programatically generated HTML and Javascript code contained in template variables. As you can imagine: instant breakage!
How to turn it off
The Django documentation does not have much good information on how to disable this new feature. Supposedly you can add the text "|safe" to every variable reference. Obviously this is impractical on even the smallest sites. Supposedly also, you can surround every template with "{% autoescape off %}" and "{% autoescape end %}" . This could be a viable option for a small site, but for someone who has to manage several sites, each with a large number of templates, this quickly becomes cumbersome. The documentation claims that the "autoescape off" setting will cascade to subclassed templates, but as of the version I have, this doesn't work.
After some grepping, I came to a temporary solution. It turns out the constructor to the Context class has an undocumented new boolean parameter called, appropriately enough, autoescape. Its default value is True. I briefly considered adding "autoescape=False" to every call to the Context constructor, but quickly abandoned that idea. The solution I came up with was to actually edit the Django source code, in trunk/django/template/context.py. I modified the constructor so that the default value of autoescape is now False. On line 12:
class Context(object):
"A stack container for variable context"
def __init__(self, dict_=None, autoescape=False):
Hopefully, Django will provide a more permanent solution in the future. Ideally, it would be a setting in the "settings.py" file. For now, this small change allows me to continue developing with this useful set of tools.
Important note (Added Feb 26, 2008)
This page is getting a lot of hits, so I want to make clear that I do not recommend making the above change to the Django source permanently. This should be viewed as a TEMPORARY fix only, until you have time to migrate all your templates and code to deal with autoescaping correctly, that is, to keep it on except where you really need it to be off.