Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relative URLs with Google App Engine and Jinja2

I use Jinja2 with Google App Engine. In am trying to use a relative path to load base.html with my edit.html template.

My theme directory structure looks like this...

  • application root
    • themes
      • default
        • admin
          • edit.html
        • base.html

I've tried this in the template...

{% extends "../base.html" %}

which returns this...

TemplateNotFound: ../base.html

When I do this...

{% extends "base.html" %}

my edit.html template loads, but without base.html.

How do I access base.html wich is one directory back from edit.html?

Now for the code...

app.yaml

application: myblog
version: 1
runtime: python27
api_version: 1
threadsafe: no

handlers:

- url: /admin/.*
  script: admin.app
  login: admin

- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  script: static.app

builtins:
- remote_api: on

libraries:
- name: webapp2
  version: "2.5.1"

- name: jinja2
  version: latest

config.py

blog_name = 'My Blog'
theme = 'default'
post_path_format = '/%(year)d/%(month)02d/%(slug)s'

admin.py

def render_template(template_name, template_vals=None, theme=None):
    template_path = os.path.join(os.path.dirname(__file__),
        'themes', theme or config.theme)
    env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_path))
    template = env.get_template(template_name)
    return template.render(**(template_vals or {}))

class BlogPost(db.Model):
    #The URL path to the blog post. Posts have a path if they are published.
    path = db.StringProperty()
    title = db.StringProperty(required=True, indexed=False)
    body = db.TextProperty(required=True)
    published = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)

    def render(self):
        template_vals = {
            'config': config,
            'post': self,
        }
        return render_template("post.html", template_vals)


form = model_form(BlogPost, Form)

class PostForm(form):
    pass

class PostHandler(webapp2.RequestHandler):

    @webapp2.cached_property
    def jinja2(self):
        return jinja2.get_jinja2(app=self.app)

    def render_to_response(
            self, template_name, template_vals=None, theme=None):
        template_name = os.path.join("admin", template_name)
        self.response.out.write(render_template(
            template_name, template_vals, theme))

    def render_form(self, form):
        self.render_to_response("edit.html", {'form': form})

    def get(self):
        self.render_form(PostForm())

app = webapp2.WSGIApplication([('/admin/newpost', PostHandler)],
                              debug=True)

edit.html

{% extends "../base.html" %}
{% block title %}Testing New Post Template{% endblock %}
{% block body %}
  <form method="post" action="">
    <table>

<div>{{ form.title.label }}: {{ form.title(class="css_class") }}</div>
{% if form.title.errors %}
        <ul class="errors">{% for error in form.name.errors %}<li>{{ error }}</li>{% endfor %}</ul>
    {% endif %}

<div>{{ form.body.label }}: {{ form.body() }}</div>
{% if form.body.errors %}
        <ul class="errors">{% for error in form.body.errors %}<li>{{ error }}</li>{% endfor %}</ul>
    {% endif %}

    </table>
    <input type="submit" />
  </form>
{% endblock %}

Full Stack Trace

ERROR    2012-09-07 07:45:42,964 webapp2.py:1553] ../base.html
Traceback (most recent call last):
  File "/home/john/google_projects/google_appengine/lib/webapp2/webapp2.py", line 1536, in __call__
    rv = self.handle_exception(request, response, e)
  File "/home/john/google_projects/google_appengine/lib/webapp2/webapp2.py", line 1530, in __call__
    rv = self.router.dispatch(request, response)
  File "/home/john/google_projects/google_appengine/lib/webapp2/webapp2.py", line 1278, in default_dispatcher
    return route.handler_adapter(request, response)
  File "/home/john/google_projects/google_appengine/lib/webapp2/webapp2.py", line 1102, in __call__
    return handler.dispatch()
  File "/home/john/google_projects/google_appengine/lib/webapp2/webapp2.py", line 572, in dispatch
    return self.handle_exception(e, self.app.debug)
  File "/home/john/google_projects/google_appengine/lib/webapp2/webapp2.py", line 570, in dispatch
    return method(*args, **kwargs)
  File "/home/john/google_projects/sprucepress/admin.py", line 86, in get
    self.render_form(PostForm())
  File "/home/john/google_projects/sprucepress/admin.py", line 83, in render_form
    self.render_to_response("edit.html", {'form': form})
  File "/home/john/google_projects/sprucepress/admin.py", line 80, in render_to_response
    template_name, template_vals, theme))
  File "/home/john/google_projects/sprucepress/admin.py", line 34, in render_template
    return template.render(**(template_vals or {}))
  File "/home/john/google_projects/google_appengine/lib/jinja2/jinja2/environment.py", line 894, in render
    return self.environment.handle_exception(exc_info, True)
  File "/home/john/google_projects/sprucepress/themes/default/admin/edit.html", line 1, in top-level template code
    {% extends "../base.html" %}
TemplateNotFound: ../base.html
like image 881
jwogrady Avatar asked Sep 07 '12 07:09

jwogrady


1 Answers

Well, I figured it out....

Here is what I learned...

  • Don't make your themes folder a static folder. This is a big no-no because on GAE static files are not available to the application. If Google App Engine is telling you the file is non accessible this may be the cause!

  • In Jinja2 all files are relative to the folder you assign to the jinja2 environment...not the template file! So, in my case... the "default" theme folder was my jinja environment's root folder. Instead of ../base.html I do base.html and it works because base.html is in the default folder that I told jinja2 to use to load templates.

  • Finally, if you want jinja2 to look for a file in multiple folders you can.

like image 152
jwogrady Avatar answered Oct 22 '22 06:10

jwogrady