Using Jinja

Jinja is a templating system. It is not unlike the templating system used by Jekyll.

In order to get jinja (actually, jinja2) working in a webapp2 application, it needs to be imported into your app file:

import os
import jinja2

And loaded into your app environment:

template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(
              loader = jinja2.FileSystemLoader(template_dir),
              autoescape = True)

There is quite a bit going on here:

os.path.dirname(__file__)

__file__ is a Python magic constant. It refers to the python fie that is currently being run.

Above, we imported the os package, which gives us access to some useful methods for getting information about your operating system.

In this case, os.path.dirname(__file__) gives the path in the file system of the current file.

os.path.join(os.path.dirname(__file__), 'templates')

os.path.join joins together path fragments to make a new path. So:

template_dir = os.path.join(os.path.dirname(__file__), 'templates')

just identifies where you are keeping your templates. In this case, in a templates directory in the same location as the current file.

jinja_env = jinja2.Environment(...)

Creates an instance of the jinja2.Environment class. This will give you access to all the methods required for rendering templates.

(loader = jinja2.FileSystemLoader(template_dir), autoescape = True)

These two arguments tell your jinja_env object where to load templates from and also to escape all content loaded into a template, i.e. automatically remove or render harmless any HTML or JavaScript code.

The next step in adding templates to your application is to create a method for returning a new HTML string using a template and some data inserted into the template in the appropriate places.

def render_str(template, **params):
    t = jinja_env.get_template(template)
    return t.render(params)

The second argument to our function is **params. The ** has a special meaning in this context in Python. It means “collect up any key-value pairs sent as arguments to the function and turn them into a dictionary”. So, params will be a dictionary that can be used in the function.

get_template is a jinja method that returns a template object. And render is a method of the template object that returns a string.

We now have a way of inserting arbitrary data into arbitrary templates.

Finally, we want to define a new render method in our handlers, which we will use instead of response.write. We do this by subclassing RequestHandler and then using this new class as the superclass for all our handlers.

class BaseHandler(webapp2.RequestHandler):
    
    def render(self, template, **kw):
        self.response.write(render_str(template, **kw))

Now we use self.render instead of self.response.write to produce output.

This may all look a bit overwhelming, but in just a few lines it gives us a lot of power. It is a good example of the uses of object-oriented programming to redefine behaviour.

Next time, we will go in to how to actually write templates in jinja.