Learning objectives
- Learn how to build and deploy an App Engine app, a simple guestbook
Prerequisites
- Basic familiarity with Python
- PC, Mac, or Linux computer with Python 2.7 installed
- The Introduction to App Engine class
- App Engine 101 in Python : the predecessor to this class
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.