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

414

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
threadsafe: true

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>
    <head>
        <title>Look Ma, I'm using Jinja!</title>
    </head>
    <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,
    loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__), 'templates')))

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!

asked 27 Apr '12, 21:55

Zaven%20Muradyan-1's gravatar image

Zaven Murady...
11.5k63560

accept rate: 79%

edited 30 Apr '12, 20:50

3

Very helpful, thanks!

(30 Apr '12, 23:07)

Timothy Gaweco

Timothy%20Gaweco's gravatar image

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)

rohit

rohit's gravatar image
8

@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)

Zaven Murady...

Zaven%20Muradyan-1's gravatar image

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

(01 May '12, 15:33)

John Horan

John%20Horan's gravatar image

(Never mind, looks like I did!)

(01 May '12, 15:34)

John Horan

John%20Horan's gravatar image

Thanks a lot!

(01 May '12, 17:03)

Zhadan Andrew

Zhadan%20Andrew's gravatar image

very helpful

(02 May '12, 22:12)

Serge M

Serge%20M's gravatar image

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

(02 May '12, 23:19)

shawn casey

shawn%20casey's gravatar image

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

(02 May '12, 23:28)

Kiran Ajbani

Kiran%20Ajbani's gravatar image

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)

Dinesh M-2

Dinesh%20M-2's gravatar image

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

(03 May '12, 14:13)

intaglioman

intaglioman's gravatar image

Thank you very much

(05 May '12, 08:13)

David Bielen

David%20Bielen's gravatar image

very helpful indeed!!

(05 May '12, 08:42)

rusydan muslih

rusydan%20muslih's gravatar image

Thanks a lot!! :)

(30 May '12, 08:03)

Tejas Pramod...

Tejas%20Pramod%20Bubane's gravatar image

You are awesome.

(15 Jul '12, 22:08)

Jason Sanchez

Jason%20Sanchez's gravatar image

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

(13 Sep '12, 20:32)

rakib

rakib's gravatar image

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

(05 Dec '12, 19:38)

Chris Bell

Chris%20Bell's gravatar image

I think your answer should be added to the official notes on using jinja

(12 Sep '13, 12:33)

Sriharsha-3

Sriharsha-3's gravatar image

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

(08 Oct '13, 03:13)

Derp

Derp's gravatar image
showing 10 of 19 show 9 more comments

32 Answers:

1234next »

I still find this confusing, as I can't get past first base! I go to google appengine: https://developers.google.com/appengine/ I am signed in.

"Open app.yaml from your AppEngine project directory in the editor of your choice"
Q1) Where is the link to get to project directory from the page I am on?! There are a multitude of links, none of which seem to say 'project directory'.

link

answered 28 Jan, 10:32

adam-35's gravatar image

adam-35
6496

Where is the project directory on google's appengine website?! Can't get past first base :-(

(28 Jan, 10:34)

adam-35

adam-35's gravatar image
  1. create a folder name "template" in your working folder
  2. move your "index.html" in this folder.
  3. keep the keyword file don't change it
  4. try again,,, it should work now

link

answered 07 Jan, 19:45

Jeff-409's gravatar image

Jeff-409
11

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

link

answered 30 Jan '13, 13:56

Michael%20Gaillard's gravatar image

Michael Gail...
1216

I'm having a similar problem. Any help here?

(03 Oct '13, 17:50)

Meg-11

Meg-11's gravatar image

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

link

answered 29 Jun '13, 17:49

Hanson%20Johnson's gravatar image

Hanson Johnson
1775

edited 29 Jun '13, 17:54

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')

link

answered 07 Jul '12, 08:35

Marco%20Alves's gravatar image

Marco Alves
9751722

Thanks man! It's very helpful!

(05 Apr '13, 13:44)

Junlin Shang

Junlin%20Shang's gravatar image

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:

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

Despite the error message, I have a folder called "templates" and, within it, created the index.html file:

enter image description here

enter image description here

enter image description here

I also have installed jinja2.

Does anyone have any idea of the cause of this error now?

link

answered 13 Jun '12, 08:20

serendipo's gravatar image

serendipo
1.4k11435

edited 13 Jun '12, 10:23

1

I get the same question! Any one?

(19 Sep '12, 01:00)

Chun Yang

Chun%20Yang's gravatar image
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

Younes-2's gravatar image

Thanks Younes-1! That did the trick! Talk about a needle in the haystack...

(13 Nov '12, 14:34)

Alexander Kr...

Alexander%20Kroeber's gravatar image

thank you soooooooooo much bro this really help me!!

(09 Jan '13, 23:27)

roberto tupa...

roberto%20tupac%20yupanqui's gravatar image

This has got to be the most useful thing I've read on this forum!!! Thank you so much!

link

answered 26 Aug '12, 22:58

Sterling%20Forest's gravatar image

Sterling Forest
311

I'm building Jinja4j a Jinja-like template engine for Java

link

answered 06 Jul '12, 15:46

Bachir%20Chihani's gravatar image

Bachir Chihani
502

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:

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

But if your index.html was auto-generated or overwritten somehow, it may now look like this:
<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
.
.
.

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.

link

answered 02 Jul '12, 13:33

Junior's gravatar image

Junior
21

edited 02 Jul '12, 13:37

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?

link

answered 12 Jun '12, 08:53

serendipo's gravatar image

serendipo
1.4k11435

1

There is a spelling error it should be 'jinja2', not 'jinga2'

(12 Jun '12, 09:00)

james gallagher

james%20gallagher's gravatar image

Correct the spelling to jinja2 (not "jinga2")

(12 Jun '12, 09:00)

sempaiscuba ♦

sempaiscuba's gravatar image
Your answer
Question text:

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "Title")
  • 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

Tags

×13,858
×9,100
×598
×192
×131
×32
×31

Asked: 27 Apr '12, 21:55

Seen: 22,553 times

Last updated: 29 Apr, 15:56