Ludicrously simple templates with Python
A long, long time ago I wrote my first ever PHP templating system. It was pretty simple; it consisted of a function that took two arguments: the name of a template file, and an associative array of replacements to make on that file.
I’ve finally got around to playing with Python CGIs for web development recently, and decided I needed a similar system. Thanks to Python’s powerful string formatting operator, it ended up as a one-liner:
def template(file, vars):
return open(templatedir.template, 'r').read() % vars
Presuming you’ve set templatedir at the top of the script, the above function lets you load a template and make some simple replacements on it with a single function call. For example:
<h3>%(title)s</h3>
%(body)s
<p class="footer">Posted: %(date)s</p>
With the above saved in the template directory as “entry.tpl”, the template function above can be used thus:
print template('entry.tpl', {
'title':'A blog entry',
'body':'Entry goes here...',
'date':'3rd July 2003'})
The work is all done by the % vars bit at the end of the line. Since vars is a dictionary, Python substitutes the named items in the dictionary for their corresponding %(varname)s tokens in the string loaded from the template file. More information on string formatting operations can be found in the manual.
As templating systems go, it’s far from the most useful or complete solution. It does however show that a little Python can go quite a long way.
tim martens - 29th July 2003 00:43 - #
Rob Hudson - 29th July 2003 06:27 - #
Hi Simon!
f you define the function like this:
def template(file, **vars):you can call it this way:
print template('entry.tpl', title="A blog entry", body="Entry goes here...", date="3rd July 2003")The
**modifier onvarscollects all remaining parameters into the dictionary.Beat Bolli - 29th July 2003 07:37 - #
Simon Willison - 29th July 2003 13:18 - #
If you're interested in something with more features, but the same basic concept in practice, Zope's DTML is worth a look.
It has controls such as IF/ELSE and IN, default values for variables and (though discouraged) the ability to include arbitrary Python code (using the EXPR attr).
Joe Grossberg - 29th July 2003 17:09 - #
If you are interested in a pure Python templating engine that is a bit easier to grasp than DTML and still very featureful, check out my website. The engine is called cmTemplate and has very complete documentation.
Yep, it's a plug, and it is certainly shameless. Sorry ;-)
C
Chris Monson - 19th August 2003 22:02 - #
Well, you could complicate it enormously, and do this to get "both" of the exciting options:
def template(file, vars={}, **extras):if extras: vars.update(extras)
return open(templatedir.template, 'r').read() % vars
This way you can pass in some pre-defined things a'la a dict, and then throw in a few changes that could override it as well. Please excuse the bad formatting, but I couldn't find any combination of tags that would allow me to embed the spacing (entity-references didn't work, pre isn't allowed).
petrilli - 7th October 2003 15:24 - #
alankar - 8th October 2003 14:12 - #
Re: DTML
Or, the newer fancier Zope Page Templates. There are a few implementations out there, not all of them need Zope at all (mine, for instance :).
In any case, ZPT is really, really nice.
<h1 tal:content='foo'>Value of foo goes here</h1>does what you would expect, and all that pesky HTML character quoting is done for you. Of course it also interpolates into tag attributes, and has all the control structures you would expect.Sune Kirkeby - 4th December 2003 08:48 - #
Bill Seitz - 20th December 2003 04:49 - #
petrilli - note that your code:
def template(file, vars={}, **extras): if extras: vars.update(extras) ... % varshas a combination of two small bugs:
vars.updateis modifying the dictionary passed to you, which might be an unpleasant surprise to the code that calls this function;varsargument, the same dictionary object (created by{}when the function was defined) is used every time and it will accumulate values.It means that if you do:
template('f', foo='fred')and thentemplate('f', bar='barney'), at the second timevarsis{'foo': 'fred', 'bar': 'barney'}keys...In this scenario it's a pretty harmless bug; I just wanted to illuminte this corner of Python. The right way is to copy the argument:
def template(file, vars={}, **extras): vars = dict(vars) # or vars.copy() vars.update(extras) # no-op if extras=={}, no if needed ... % varsAnd in Python 2.3 and later you can let
dictdo the merging:def template(file, vars={}, **extras): ... % dict(vars, **extras)Beni Cherniavsky - 17th March 2005 23:00 - #
Outsourcing - 8th March 2006 07:14 - #