# [HOW-TO] Use the Jinja2 template engine with AppEngine

If you're like me, then you dislike mixing programming with markup. So, naturally, you want to take the HTML that you use to generate your web page out of your Python code and into a template document. Well, AppEngine comes with a templating engine that can help you do just that!

# Jinja2

Jinja2 is a powerful templating engine modeled after Django's templating system. The idea is to separate your logic from your presentation, and make your code clean and well-defined in the process.

Here's the basic process to get it working in AppEngine.

### Step 1: Modify app.yaml

Open app.yaml from your AppEngine project directory in the editor of your choice. You need to do a few things here: 1) change the runtime attribute to python27, 2) add threadsafe: true, and 3) add the Jinja2 library in the configuration. Here's what your app.yaml might look like after you're done:

application: foobar
version: 1
runtime: python27
api_version: 1

libraries:
- name: jinja2
version: latest

handlers:
- url: /.*
script: main.app


The important bit is the libraries section - that's where you grant access to Jinja2.

### Step 2: Move your HTML to a separate file

For the sake of this example, let's create a directory in your project folder called templates. Place your HTML code into a file called, let's say, index.html, underneath your templates folder.

Note: do not add a static_dir URL handler in your app.yaml file. A static_dir handler is used to expose a directory of files to the client - things like images, static JavaScript files, stylesheets, etc. It turns out that AppEngine stores these files in a separate location than your app files, for efficiency reasons. This means that if you unintentionally set, say, your templates directory as a static_dir, then your app will throw an error every time you try to use a template, because the files won't be accessible!

Now, you have to add template placeholders to your HTML. Jinja2 has a default (and quite rich) syntax that it uses to generate filled-in HTML from your templates. Here's a simple example:

<!DOCTYPE html>
<html>
<title>Look Ma, I'm using Jinja!</title>
<body>
Hi there - I'm {{ name }}, and I {{ verb }} programming!
</body>
</html>


The placeholders {{ name }} and {{ verb }} are what Jinja2 replaces with the data that your app provides. Remember these names - you'll have to use them when you send data to the templating engine. Almost done!

### Step 3: Modify your main Python script to use Jinja2

Obviously, if you haven't already, remove the form = """<html... stuff from your Python file - that's what the template is for.

Now we must import the jinja2 module, and create an "environment" using which we can access the templating functions. Here's what the top section of your script might look like:

import os
import webapp2

import jinja2

jinja_environment = jinja2.Environment(autoescape=True,


A few things to note here: the jinja2.Environment object that we're instantiating takes a loader argument, which takes a directory from which it can load templates; we've imported the os module just so that we can use its path manipulation functions to get the path to our templates directory. Notice also that we've used the autoescape=True argument - this makes Jinja2 escape all dangerous strings for us, so we don't have to!

Finally, to use the template in one of our request handlers, use something like this:

class MainPage(webapp2.RequestHandler):
def get(self):
template_values = {
'name': 'SomeGuy',
'verb': 'extremely enjoy'
}

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


First we create a dictionary with the name/value pairs for the template - notice that the keys are the same as those included in the template. Then we retrieve a template object using jinja_environment.get_template, and finally we render and write out the resulting HTML using template.render(template_values).

And that's it! Try it out!

10.7k62859
accept rate: 79%

3

(30 Apr '12, 23:07)

Thanks dude it was really helpful...I had been pulling my hair off reading the docs at jinja2 docs...Just tell me one more thing...The jinja template is already iinstalled in app engine ???
why dont we have to install it??

(01 May '12, 13:30)
7

@rohit: Yes, Jinja2 is packaged with AppEngine, so you don't have to manually install it. You just have to declare that you want to use it in your app.yaml

(01 May '12, 15:15)

Thank you, @voithos. [Couldn't figure out how to add a link to your name in this comment....]

(01 May '12, 15:33)

(Never mind, looks like I did!)

(01 May '12, 15:34)

Thanks a lot!

(01 May '12, 17:03)

(02 May '12, 22:12)

How do I link the main.app to a main.py?

(02 May '12, 23:19)

Thanks buddy this was very helpful... would suggest to append with CSS usage also

(02 May '12, 23:28)

Thank you so much for the pain free tutorial. You saved my day. I would vote you a 100 times it was possible.

(03 May '12, 10:45)

Awesome, Thanks for this. It worked like a charm.

(03 May '12, 14:13)

Thank you very much

(05 May '12, 08:13)

(05 May '12, 08:42)

Thanks a lot!! :)

(30 May '12, 08:03)

You are awesome.

(15 Jul '12, 22:08)

thanks man....... i love educators like you........ clear stated every step as required...... thanks

(13 Sep '12, 20:32)

thanks so much for this great explanation. My app is converted to using jinja templates.

(05 Dec '12, 19:38)

(12 Sep, 12:33)

what if i have a placeholder that has an apostrophe? how would i escape it in the python file

(08 Oct, 03:13)
showing 10 of 19 show 9 more comments

 2 I did everything this post sais and I still get an error... Can anyone please help me? This is a bit frustrating... Server error The website encountered an error while retrieving http://localhost:8081/. It may be down for maintenance or configured incorrectly. Here are some suggestions: Reload this webpage later. HTTP Error 500 (Internal Server Error): An unexpected condition was encountered while the server was attempting to fulfill the request. This is what my yaml file looks like(I also did a copy-paste of the instructions given by Zaven and it did not work) : application: mpgaillard version: 1 runtime: python27 api_version: 1 threadsafe: yes handlers: - url: /favicon\.ico static_files: favicon.ico upload: favicon\.ico - url: /.* script: main.app libraries: - name: webapp2 version: "2.5.2" - name: jinja2 version: "2.6" main.py: import os import webapp2 import jinja2 jinja_environment = jinja2.Environment(autoescape=True, loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(file), 'templates'))) class MainPage(webapp2.RequestHandler): def get(self): template_values = { 'name': 'SomeGuy', 'verb': 'extremely enjoy' }  template = jinja_environment.get_template('index.html') self.response.out.write(template.render(template_values)  app = webapp2.WSGIApplication([('/', MainPage)], debug=True) I am running OSX Lion. I also went on this site: http://pypi.python.org/pypi/Jinja2 and downloaded Jinja2, extracted the compressed file and I went to that folder using the terminal and did sudo python setup.py install and I still can't get it to work... answered 30 Jan, 13:56 Michael Gail... 106●6 I'm having a similar problem. Any help here? (03 Oct, 17:50) Meg-11
 0 I am sorry if I have broken the rules by asking a question instead of answering. @Zaven please I need your help in this my question answered 29 Jun, 17:49 Hanson Johnson 137●5
 1 I've created a BaseHandler class that provides: Method for rendering templates: render_response User id cookie handling: (1) write cookie with user id hash, (2) check if request has valid user id hash and (3) get the user id for db gets Default redirections for get and posts that do not have valid user id hashes Below is the BaseHandler class: class BaseHandler(webapp2.RequestHandler): USERID_COOKIE = "USERID" @webapp2.cached_property def jinja2(self): # Returns a Jinja2 renderer cached in the app registry. return jinja2.get_jinja2(app=self.app) def render_response(self, _template, **context): # Renders a template and writes the result to the response. rv = self.jinja2.render_template(_template, **context) self.response.write(rv) def write_cookie(self, cookie_name, cookie_data = ""): # write or rewrites cookie with cookie_dat if cookie_data: cookie_data = make_hash(str(cookie_data)) self.response.headers.add_header( "Set-Cookie", "%(name)s=%(data)s; Path=/" % { "name": cookie_name, "data": cookie_data} ) def request_has_valid_cookie(self): # returns true if cookie has valid user id hash cookie_data = self.request.cookies.get(self.USERID_COOKIE) if cookie_data: if check_secure_val(cookie_data): return True return False def get_user_id(self): # if the user cookie has valid user id hash, return the original user id cookie_data = self.request.cookies.get(self.USERID_COOKIE) if cookie_data: user_id = check_secure_val(cookie_data) return int(user_id) def get(self, *args): # if request does not have valid user id cookie, than redirect to default # else let the Get method handle the request if self.request_has_valid_cookie(): self.Get(*args) else: self.render_response('home.html') def post(self, *args): # if request does not have valid user id cookie, than redirect to default # else let the Post method handle the request if self.request_has_valid_cookie(): self.Post(*args) else: self.render_response('home.html')  An example of handler that extends the BaseHandler funcionality: class MainHandler(BaseHandler): def Get(self): if self.get_user_id(): self.redirect("/userpage") else: self.render_response('home.html')  answered 07 Jul '12, 08:35 Marco Alves 975●1●7●22 Thanks man! It's very helpful! (05 Apr, 13:44) Junlin Shang
 2 I'm trying to build my first GAE app with jinja2. After overcoming a dozen small errors, now I'm stuck with this: Traceback (most recent call last): File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1536, in __call__ rv = self.handle_exception(request, response, e) File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1530, in __call__ rv = self.router.dispatch(request, response) File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1278, in default_dispatcher return route.handler_adapter(request, response) File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1102, in __call__ return handler.dispatch() File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 572, in dispatch return self.handle_exception(e, self.app.debug) File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 570, in dispatch return method(*args, **kwargs) File "C:\Users\CG\Documents\udacity\HiMon\main.py", line 31, in get template = jinja_environment.get_template('index.html') File "C:\Program Files (x86)\Google\google_appengine\lib\jinja2\jinja2\environment.py", line 719, in get_template return self._load_template(name, self.make_globals(globals)) File "C:\Program Files (x86)\Google\google_appengine\lib\jinja2\jinja2\environment.py", line 693, in _load_template template = self.loader.load(self, name, globals) File "C:\Program Files (x86)\Google\google_appengine\lib\jinja2\jinja2\loaders.py", line 115, in load source, filename, uptodate = self.get_source(environment, name) File "C:\Program Files (x86)\Google\google_appengine\lib\jinja2\jinja2\loaders.py", line 180, in get_source raise TemplateNotFound(template) TemplateNotFound: index.html  Here my yaml file: application: himother version: 1 runtime: python27 api_version: 1 threadsafe: yes handlers: - url: /favicon\.ico static_files: favicon.ico upload: favicon\.ico - url: .* script: main.app libraries: - name: webapp2 version: "2.5.1" - name: jinja2 version: "2.6"  Here my code: import os import webapp2 import jinja2 jinja_environment = jinja2.Environment(autoescape=True, loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__), 'templates'))) class MainPage(webapp2.RequestHandler): def get(self): template_values = { 'name': 'Serendipo', 'verb': 'extremely happy' } template = jinja_environment.get_template('index.html') self.response.out.write(template.render(template_values)) app = webapp2.WSGIApplication([('/', MainPage)], debug=True)  Here my .html template:  Look Ma, I'm using Jinja! Hi there - I'm {{ name }}, and I {{ verb }} programming!  Despite the error message, I have a folder called "templates" and, within it, created the index.html file: I also have installed jinja2. Does anyone have any idea of the cause of this error now? answered 13 Jun '12, 08:20 serendipo 1.4k●1●14●33 1 I get the same question! Any one? (19 Sep '12, 01:00) Chun Yang 1 you are hidding txt extension. actually your file name is "index.html.txt". in explorer go to -> organize -> folder and search options -> view and then uncheck "hide extensions for known file types. then remove txt from the end, and you'll be fine. (19 Sep '12, 19:38) Younes-2 Thanks Younes-1! That did the trick! Talk about a needle in the haystack... (13 Nov '12, 14:34) Alexander Kr... thank you soooooooooo much bro this really help me!! (09 Jan, 23:27) roberto tupa...
 2 This has got to be the most useful thing I've read on this forum!!! Thank you so much! answered 26 Aug '12, 22:58 Sterling Forest 31●1
 0 I'm building Jinja4j a Jinja-like template engine for Java answered 06 Jul '12, 15:46 Bachir Chihani 50●2
 0 I had a similar problem. Check to make sure your index.html file in the templates folder is what you think it is. Referencing Appendix B of the course, it should look like this: Look Ma, I'm using Jinja! Hi there - I'm {{ name }}, and I {{ verb }} programming! But if your index.html was auto-generated or overwritten somehow, it may now look like this: . . . Edit: my example code from the Appendix seems to be all fowled up with some escaping here. Go to the Appendix to see the referenced code for index.html. answered 02 Jul '12, 13:33 Junior 2●1
 0 I tried to follow the steps in this tutorial and apparently the Jinga2 installation by CMD occurred normally, but still I encounter this error in the GAE "logs": ERROR 2012-06-12 12:46:26,786 dev_appserver_main.py:580] Fatal error when loading application configuration: Invalid object: the library "jinga2" is not supported in "C:\Users\CA\Documents\udacity\contract\app.yaml", line 19, column 17 2012-06-12 09:46:26 (Process exited with code 1)  Any suggestions for how to bypass this error? answered 12 Jun '12, 08:53 serendipo 1.4k●1●14●33 1 There is a spelling error it should be 'jinja2', not 'jinga2' (12 Jun '12, 09:00) james gallagher Correct the spelling to jinja2 (not "jinga2") (12 Jun '12, 09:00) sempaiscuba ♦
 0 I am slightly behind in the units, so I am just getting to Unit 3 and was having a great deal of difficulty getting Jinja2 installed on my Mac. Mostly, it seemed to be a permissions issue, however I was still having problems from my admin account. So, I did some looking around and found the following link: This was quick and painless. I got it installed and working. So, if anyone out there is still having troubles installing, try this. Best to all, and good luck! answered 16 May '12, 10:40
 0 When I tried to set threadsafe to true (or alternatively "yes"), I got the following error when I loaded the page: Fatal error when loading application configuration: Invalid object: threadsafe cannot be enabled with CGI handler: helloworld.py in "C:\Users\__\workspace\take2\src\app.yaml", line 14, column 1  any insight? It seems to work OK with threadsafe: no, but I don't know why. And by the way, what does this setting do? answered 08 May '12, 06:22 Allen Nelson 238●6 What is your Python version and OS? (08 May '12, 06:37) Joe Python 2.7.3, Windows 7 (08 May '12, 06:38) Allen Nelson @Allen: threadsafe is used to indicate that the script can be used with concurrent requests. You can't do so if you're using CGI, you need to use WSGI. In your app.yaml file handlers section, you're probably listing the script as helloworld.py - it needs to be helloworld.app` (notice the extension). More info here. (08 May '12, 20:46) Zaven Murady...
Question text:

Markdown Basics

• *italic* or _italic_
• **bold** or __bold__
• image?![alt text](/path/img.jpg "Title")
• numbered list: 1. Foo 2. Bar
• to add a line break simply add two spaces to where you would like the new line to be.
• basic HTML tags are also supported

×12,815
×9,049
×587
×189
×126
×31
×29