Google App Engine

Using Templates

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. 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.

Using Jinja2 Templates

First modify the libraries section at the bottom of guestbook/app.yaml:

Run / Modifylibraries:
- name: webapp2
  version: latest
- 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 modify the statements at the top of guestbook/

Run / Modifyimport os
import urllib

from google.appengine.api import users
from google.appengine.ext import ndb

import jinja2
import webapp2

JINJA_ENVIRONMENT = jinja2.Environment(

Replace the MainPage handler with code that resembles the following:

Run / Modifyclass MainPage(webapp2.RequestHandler):

    def get(self):
        guestbook_name = self.request.get('guestbook_name',
        greetings_query = Greeting.query(
        greetings = greetings_query.fetch(10)

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

        template_values = {
            'greetings': greetings,
            'guestbook_name': urllib.quote_plus(guestbook_name),
            'url': url,
            'url_linktext': url_linktext,

        template = JINJA_ENVIRONMENT.get_template('index.html')

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

Run / Modify<!DOCTYPE html>
{% autoescape true %}
    {% for greeting in greetings %}
      {% if %}
        <b>{{ }}</b> wrote:
      {% else %}
       An anonymous person wrote:
      {% endif %}
      <blockquote>{{ greeting.content }}</blockquote>
    {% endfor %}

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


    <form>Guestbook name:
      <input value="{{ guestbook_name }}" name="guestbook_name">
      <input type="submit" value="switch">

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

{% endautoescape %}

Reload the page, and try it out.

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.

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".


Every web application returns dynamically generated HTML from the application code, via templates or some other mechanism. Most web applications also need to serve static content, such as images, CSS stylesheets, or JavaScript files. For efficiency, App Engine treats static files differently from application source and data files. You can use App Engine's static files feature to serve a CSS stylesheet for this application.

Continue to Using Static Files.

Authentication required

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

Signing you in...

Google Developers needs your permission to do that.