Notice: Over the next few months, we're reorganizing the App Engine documentation site to make it easier to find content and better align with the rest of Google Cloud products. The same content will be available, but the navigation will now match the rest of the Cloud products. If you have feedback or questions as you navigate the site, click Send Feedback.

Python 2 is no longer supported by the community. We recommend that you migrate Python 2 apps to Python 3.

Generating Dynamic Content from Templates

Stay organized with collections Save and categorize content based on your preferences.

This part of the Python Guestbook code walkthrough shows how to use Jinja templates to generate dynamic web content.

This page is part of a multi-page tutorial. To start from the beginning and see instructions for setting up, go to Creating a Guestbook.

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

The app.yaml file lists the latest version of jinja2 as a required library. Production applications should use an actual version number rather than version: latest.

- name: webapp2
  version: latest
- name: jinja2
  version: latest

The app imports jinja2 and creates a jinja2.Environment object.

import os
import urllib

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

import jinja2
import webapp2

JINJA_ENVIRONMENT = jinja2.Environment(

The get method for the MainPage request handler forms a dictionary of key/value pairs and passes it to template.render.

class MainPage(webapp2.RequestHandler):

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

        user = users.get_current_user()
        if 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 = {
            'user': user,
            'greetings': greetings,
            'guestbook_name': urllib.quote_plus(guestbook_name),
            'url': url,
            'url_linktext': url_linktext,

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

The page is rendered according to the index.html template, which receives the dictionary as input.

{% for greeting in greetings %}
<div class="row">
  {% if %}
    <b>{{ }}
      {% if user and user.user_id() == %}
      {% endif %}
    </b> wrote:
  {% else %}
    An anonymous person wrote:
  {% endif %}
  <blockquote>{{ greeting.content }}</blockquote>
{% endfor %}

The JINJA_ENVIRONMENT.get_template(name) method takes the name of a template file and returns a template object. The template.render(template_values) call 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.