In this reading, we'll look at using a Python package
called Jinja2
to using templating.
Why use templating at all? Most websites have many pages, but with a common look and infrastructure (CSS files, JS files, etc). There's actually quite a lot of that boilerplate in this reading file (do a "view source" to see). Unfortunately, I haven't (yet) adopted a templating scheme for the CS 304 website, so if I decide to add or remove an infrastructure file, or to change some of the common markup, I have to update all umpteen pages of this site. It's a nightmare.
So, don't make my mistake. If the common boilerplate is saved in a file, called a template, our web application (our Python code) can dynamically fill in the variable stuff (such as information pulled from the database). If we need to change the common stuff, it can easily be done by editing just one file.
Last week, we looked at a simple approach to templating
using Python's
built-in string formating method, namely .format()
:
templ = 'The actor {name} was born on {date} and ...' data = {'name': 'George Clooney', 'date': 'May 6, 1961'} combo = templ.format(**data) print(combo)
What's wrong with that? We'll look at a few issues to motivate this topic, then I'll punt to online tutorials and resources to teach Jinja2 itself.
By definition, a template has parts that need to be filled in with
dynamic data. I'm not sure if there is a standard term for the parts
of the template string that are surrounded by braces. The
documentation for Python's method called them replacement
fields
. The documentation for Jinja2 calls them tags
. The
syntax of the two templates is different as well, and that syntax
matters.
The syntax for Python's replacement fields is just a pair of braces, and that syntax is unfortunately very common in other languages. Imagine a template file that also contains some JavaScript or some CSS:
<style> aside { width: 10%; float: left; } ... </style> <aside>{aside}</aside> <script> $("aside").click(function () { $(this).hide(); }); </script>
Imagine that all that and more is saved in a file
called page.tmpl
. We could imagine filling in the
contents of the aside very easily:
tmpl = cgi_utils_sda.file_contents('page.tmpl') data = {'aside': 'Actors list:
'+getActors()} combo = tmpl.format(**data) print(combo)
If you try that, you'll get an error because your data
only fills in one of the three replacement tags in
the template.
Wait? three? Do you see the three replacement tags in the template file? They're easy to overlook except that I've sensitized you to them.
There are other workarounds to this problem, simpler than using a templating engine. You could
If none of the other advantages of Jinja2 persuade you, you might
decide to stick to Python's .format()
method. There's
nothing wrong with that.
Python's .format
method just inserts a value into a
string, but Jinja2 allows you to do much more powerful things. For
example, if you have an array of strings, you can format them
like this.
Here's the template file:
<p>Here is a list of {{description}}: <ul> {% for person in people %} <li>{{ person }}</li> {% endfor %} </ul>
Here's the Python code:
#!/usr/local/bin/python2.7 from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader('./')) tmpl = env.get_template('actorlist.tmpl') actors = ['George Clooney', 'Salma Hayek', 'Jennifer Lopez', 'Colin Firth'] combo = tmpl.render(description='Actors',people=actors) print(combo)
Clearly, it's a big advantage to be able to move the formatting code into the templating system, rather than to have to do that in the Python code. On the other hand, you have to learn yet another language. Fortunately, it's similar to Python.
Very often, the pages of a website have an overall theme, but with variations:
How to balance all this? If we use Python's .format()
method, each kind of page will have to have its own template file, all
of which will share the common infrastructure code, so if we change
that infrastructure, every template file will need to be updated. This
is way better than having to update every page, but it still
allows errors to creep in (you update some but not all of the
templates).
A better, more sophisticated, approach is to use inheritance,
where a base template is very abstract, with chunks (blocks
)
that are filled in by the child templates. For example, the CS 304
site could have a base template that supplies the overall structure,
CSS, jQuery and JavaScript files. Then child templates could be used
for readings (like this file), lecture activities, assignments, and
other kinds of pages.
Jinja2 allows inheritance like this. Look for it in the readings below.
Jinja2 is closely associated with the Flask micro-framework, even though it is separable. Many of the tutorials out there assume that you're using Jinja2 in the context of Flask. Since we will be doing so, that's not a disadvantage.
Here is a good introduction. If you find a better one, please let me know.
If you're confused, you might try these, but they are optional and not expected.