Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)



Using Templates

Learning objectives
  • Learn how to build and deploy an App Engine app, a simple guestbook
Prerequisites
Related

Amy Unruh, Dan Sanderson, Oct 2012
Google Developer Relations

Introduction

Nearly all web application request handlers serve up dynamically generated content. In the previous lesson, we included some HTML in the handler method itself. However, HTML embedded in code is messy and difficult to maintain; it's better to use a templating system, where the HTML is kept in a separate file with special syntax to indicate where the data from the application appears. In this lesson, we'll look at an easy way to do that, using the Jinja2 templating system.

Using Jinja2 Templates

There are many templating systems for Python: EZT , Cheetah , Clearsilver , Quixote , Django , and Jinja2 are just a few. You can use your template engine of choice by bundling it with your application code. For your convenience, App Engine includes the Django and Jinja2 templating engines. Here, we'll use Jinja2.

First, add the jinja2 library to the bottom of helloworld/app.yaml , like this:

libraries:
- name: jinja2
  version: latest

This configuration makes the newest supported version of Jinja2 available to your application. To avoid possible compatibility issues, serious applications should use an actual version number rather than latest .

Now add the following statements at the top of helloworld/main.py .

import jinja2
import os

jinja_environment = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))

Next, replace the MainPage handler with code like the following:

class MainPage(webapp2.RequestHandler):
  def get(self):
    guestbook_name = self.request.get('guestbook_name')
    # There is no need to actually create the parent Book entity; we can
    # set it to be the parent of another entity without explicitly creating it
    ancestor_key = ndb.Key("Book", guestbook_name or "*notitle*")
    greetings = Greeting.query_book(ancestor_key).fetch(20)

    if users.get_current_user():
      url = users.create_logout_url(self.request.uri)
      url_linktext = 'Logout'
    else:
      url = users.create_login_url(self.request.uri)
      url_linktext = 'Login'

    template_values = {
      'greetings': greetings,
      'url': url,
      'url_linktext': url_linktext,
      'guestbook_name': guestbook_name

    }

    template = jinja_environment.get_template('index.html')
    self.response.out.write(template.render(template_values))

You can see that we've added code that generates a login or logout URL depending upon the requesting user's logged-in status. We add this information to a template_values dictionary, along with the list of greetings .

Finally, create a new file in the helloworld directory named index.html , with the following contents:

<html>
  <body>
    {% for greeting in greetings %}
      {% if greeting.author %}
        <b>{{ greeting.author.nickname() }}</b> wrote:
      {% else %}
        An anonymous person wrote:
      {% endif %}
      <blockquote>{{ greeting.content|escape }}</blockquote>
    {% endfor %}

    <form action="/sign" method="post">
      <input type="hidden" value="{{guestbook_name}}" name="guestbook_name">
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Sign Guestbook"></div>
    </form>
    <form>Guestbook name: <input value="{{guestbook_name}}" name="guestbook_name">
    <input type="submit" value="switch"></form>

    <a href="{{ url}}">{{ url_linktext }}</a>

  </body>
</html>

This is the template for your index page. At the end of the new MainPage request handler above, you can see that this template is rendered using the template_values dictionary that you created.

jinja_environment.get_template(name) takes the name of a template file and returns a template object. template.render(template_values) takes a dictionary of values and returns the rendered text. The template uses Jinja2 templating syntax to access and iterate over the values, and can refer to properties of those values. In many cases, you can pass Datastore model objects directly as values and access their properties from templates.

Reload the page and try it out. See what happens when you log out or log in and then add a new guestbook entry.

Tip : An App Engine application has read-only access to all of the files uploaded with the project, the library modules, and no other files. The current working directory is the application root directory, so the path to index.html is simply "index.html" .

Summary and Review

In this lesson, we've used Jinja2 templating to generate the MainPage request handler's response from a template file. Try making a few small changes to the templating and see if the results are as you expect. For example, change anonymous to unknown in index.html and reload. Or try changing the name of a key in the template_values dictionary: for example, change greetings to greetings_list in the code. Can you see where you need to change the template accordingly so that it still works?

In the next lesson , we'll look at how to serve static content.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.