I'm getting a TemplateNotFound error on Google App Engine with Jinja2 (complete stack trace below.)
What I expect to see is the index.html with the "greet" variable passed to the index.html template file. What I don't understand is why I get the template not found error when the path to index.html in the TraceBack is correct.
What I've tried...
tried a relative path by taking out "os.path.dirname(file)" in template path.
using "template" instead of themes as a directory name.
Here is my code.
app.yaml
application: codemywayout
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /admin/.*
script: admin.app
login: admin
- url: /static/([^/]+)/(.*)
static_files: template/\1/static/\2
upload: static/.*
- 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
admin.py
from google.appengine.ext import db
import webapp2
import jinja2
import os
import fix_path
import config
def render_template(template_name, template_vals=None, theme=None):
template_path = os.path.join(os.path.dirname(__file__) , \
"themes", theme or config.theme, template_name)
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_path))
return env.get_template(template_path, template_vals or {})
class BlogPost(db.Model):
title = db.StringProperty()
body = db.StringProperty()
def render(self):
template_vals = {
'config': config,
'post': self,
}
return render_template("post.html", template_vals)
class BaseHandler(webapp2.RequestHandler):
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))
class AdminHandler(BaseHandler):
def get(self):
greet = "hello"
template_vals = {
'greet': greet
}
self.render_to_response("index.html", template_vals)
config.py
# Name of the blog
blog_name = 'My Blog'
# Selects the theme to use. Theme names correspond to directories under
# the 'themes' directory, containing templates and static content.
theme = 'default'
# Defines the URL organization to use for blog postings. Valid substitutions:
# slug - the identifier for the post, derived from the title
# year - the year the post was published in
# month - the month the post was published in
# day - the day the post was published in
# URL Options
# post_path_format = '/%(year)d/%(month)02d/%(slug)s'
post_path_format = '/%(slug)s'
TraceBack
Traceback (most recent call last):
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1536, in __call__
rv = self.handle_exception(request, response, e)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1530, in __call__
rv = self.router.dispatch(request, response)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1278, in default_dispatcher
return route.handler_adapter(request, response)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 1102, in __call__
return handler.dispatch()
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 572, in dispatch
return self.handle_exception(e, self.app.debug)
File "C:\Users\john\webdev\google\lib\webapp2\webapp2.py", line 570, in dispatch
return method(*args, **kwargs)
File "C:\Users\john\webdev\workspace\codemywayout\admin.py", line 49, in get
self.render_to_response("index.html", template_vals)
File "C:\Users\john\webdev\workspace\codemywayout\admin.py", line 34, in render_to_response
template_vals, theme))
File "C:\Users\john\webdev\workspace\codemywayout\admin.py", line 14, in render_template
return env.get_template(template_path, template_vals or {})
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\environment.py", line 719, in get_template
return self._load_template(name, self.make_globals(globals))
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\environment.py", line 693, in _load_template
template = self.loader.load(self, name, globals)
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py", line 115, in load
source, filename, uptodate = self.get_source(environment, name)
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py", line 162, in get_source
pieces = split_template_path(template)
File "C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py", line 33, in split_template_path
raise TemplateNotFound(template)
TemplateNotFound: C:\Users\john\webdev\workspace\codemywayout\themes\default\admin\index.html
I hope your not trying to load the Jinja template from the same path as your static files
static_files: template/\1/static/\2
As that path is not accessible to your application.
I would try and log the path that Jinja is trying to load the template from to help you understand where it is trying to load the template.
If you look at C:\Users\john\webdev\google\lib\jinja2\jinja2\loaders.py line 33 you can probably set a pdb breakpoint import pdb; pdb.set_trace( ) and see what's going on.
I'm using the SDK source distribution on a Mac, but if your code is the same as what I see, it's:
def split_template_path(template):
"""Split a path into segments and perform a sanity check. If it detects
'..' in the path it will raise a `TemplateNotFound` error.
"""
pieces = []
for piece in template.split('/'):
if path.sep in piece \
or (path.altsep and path.altsep in piece) or \
piece == path.pardir:
raise TemplateNotFound(template)
elif piece and piece != '.':
pieces.append(piece)
return pieces
Note the for piece in template.split('/') where it splits on '/' instead of path.sep. I suspect that your input is a C:\Path\to\file.html in which case piece is the entire path, adn path.sep (\ on windows) is indeed in piece, which gives a TemplateNotFound error.
Instead of template_name = os.path.join("admin", template_name) you could try template_name = 'admin/%s' % template_name, don't include template_name in the template_path and pass template_name instead of template_path into env.get_template(template_name, template_vals or {}) as loaders.py line 162 indicates that it looks for template_name in template_path
Your code, modified but untested would look something like
from google.appengine.ext import db
import webapp2
import jinja2
import os
import fix_path
import config
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))
return env.get_template(template_name, template_vals or {})
class BlogPost(db.Model):
title = db.StringProperty()
body = db.StringProperty()
def render(self):
template_vals = {
'config': config,
'post': self,
}
return render_template("post.html", template_vals)
class BaseHandler(webapp2.RequestHandler):
def render_to_response(self, template_name, \
template_vals=None, theme=None):
template_name = "admin/%s" % template_name
self.response.out.write(render_template(template_name,\
template_vals, theme))
class AdminHandler(BaseHandler):
def get(self):
greet = "hello"
template_vals = {
'greet': greet
}
self.render_to_response("index.html", template_vals)
Related
I am learning Google App Engine, and have started with WebApp2 framework + Jinja2 template.
I have written the following code:
Main.py
import webapp2
import os
import jinja2
from google.appengine.ext.webapp import template
from google.appengine.api import users
from google.appengine.ext import ndb
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir))
class Handler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self, template, **params):
t = jinja_env.get_template(template)
return t.render(params)
def render(self, template, **kw):
self, write(self.render_str(template, **kw))
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.write('Hello world!')
class PageOne(Handler):
def get(self):
self.render('pageone.html')
app = webapp2.WSGIApplication([('/', MainHandler),('/pageone', PageOne)], debug=True)
app.yaml
application: tbapp
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: latest
There is also a pageone.html HTML file in templates folder.
Now, when I run the files, the main page is showing Hello world!. But, when I run http://localhost:8080/pageone..... it returns the following error..
File "E:\gae_apps\tbapp\tbapp\main.py", line 50, in get
self.render('pageone.html')
File "E:\gae_apps\tbapp\tbapp\main.py", line 40, in render
self, write(self.render_str(template, **kw))
NameError: global name 'write' is not defined
Can anyone please guide me, what am In doing wrong. I am not able to figure out. TIA
As Amber pointed out, you have a typo in your Handler's render method.
Replace
def render(self, template, **kw):
self, write(self.render_str(template, **kw))
With
def render(self, template, **kw):
self.write(self.render_str(template, **kw))
The error message does give you a hint
NameError: global name 'write' is not defined
I'm trying to add chat to my site and am integrating some code with my existing code. The chat app works fine on its own when it's all set up in the original main.app file. But when I try to move that same code to a handlers.py file and then setup up routes in routes.py I get errors saying template variables are undefined. Are the two different codes conflicting in the way they render templates? They seem to be using webapp2 differently, i.e. my code renders templates like this:
self.render_template('secure_zone.html', **params)
And the chat app like this:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
Are both acceptable?
Here's my handlers.py file:
Routes are setup in routes.py and added in main.py
"""
import httpagentparser
from boilerplate import models
from boilerplate.lib.basehandler import BaseHandler
from boilerplate.lib.basehandler import user_required
class SecureRequestHandler(BaseHandler):
"""
Only accessible to users that are logged in
"""
#user_required
def get(self, **kwargs):
user_session = self.user
user_session_object = self.auth.store.get_session(self.request)
user_info = models.User.get_by_id(long( self.user_id ))
user_info_object = self.auth.store.user_model.get_by_auth_token(
user_session['user_id'], user_session['token'])
try:
params = {
"user_session" : user_session,
"user_session_object" : user_session_object,
"user_info" : user_info,
"user_info_object" : user_info_object,
"userinfo_logout-url" : self.auth_config['logout_url'],
}
return self.render_template('secure_zone.html', **params)
except (AttributeError, KeyError), e:
return "Secure zone error:" + " %s." % e
Here's the main.py file for the chat app:
import os
import hashlib
import urllib
import logging
import re
import json
import webapp2
import jinja2
from google.appengine.api import channel as channel_api # 'channel' is kind of ambiguous in context
from google.appengine.ext import db
from google.appengine.api import memcache
# This section will eventually get moved to a Handler class
template_dir = os.path.join(
os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(
loader = jinja2.FileSystemLoader(template_dir),
autoescape = True)
def render_str(template, **params):
'''Returns a string of the rendered template'''
t = jinja_env.get_template(template)
return t.render(params)
def render(template, **kw):
'''Render using the template and parameters'''
return(render_str(template, **kw))
# End Handler
class Main(webapp2.RequestHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
def post(self):
'''Displays chat UI'''
username = self.request.get('username')
channelname = self.request.get('channel')
usernameerror = ""
if not username:
usernameerror="Please enter a username"
elif not re.compile(r'^[a-zA-Z0-9_-]{3,20}$').match(username):
usernameerror = "Username must consist of 3-20 alphanumeric characters."
elif get_user(username):
usernameerror="Username already in use"
channelerror = ""
if channelname and not re.compile(r'^#[\w]{3,20}$').match(channelname):
channelerror="Channel must consist of 3-20 alpha_numeric characters and start with a #"
if len(usernameerror+channelerror) > 0:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
app = webapp2.WSGIApplication([
('/', Main),
('/communication', Communication),
('/_ah/channel/connected/?', Connect),
('/_ah/channel/disconnected/?', Disconnect)
], debug=True)
The specific error you posted in the comments "Error: 'Main' object has no attribute 'render_template'" is because in your Main handler, you try to return self.render_template. You should be just calling the function like this:
render_template("main.html", channel="#udacity")
Please note that I did not check the rest of your code, so if you run into any other issues, please post the specific errors you get.
It is because your webapp2.RequestHandler do not have the corresponding function "render_template"
you can use a BaseHandler with render_template function added to achieve the template rendering
from google.appengine.ext.webapp import template
class BaseHandler(webapp2.RequestHandler):
def render_template(self, filename, **template_args):
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
self.response.write(template.render(path, template_args))
class Main(BaseHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
ref: http://blog.notdot.net/2011/11/Migrating-to-Python-2-7-part-2-Webapp-and-templates
I just started using webapp2 + python 2.7 + Jinja2 few days, and that is the same problem I encountered.
Hope this solution can help you ;)
I have been racking my brain for weeks trying to get this authentication module to work under the webapp2 framework using Jinja, NDB and WTForms. I've looked online everywhere and have reached my breaking point and need to reach out to somebody for help. I've been following this tutorial (which is pretty thorough but I think leaves out some important details, especially for a newbie like me):
User Accounts with Webapp2 + Google App Engine
As far as I know I have everything setup correctly but I continually get a 404 - resources can not be found. What could I be doing wrong? Here is my code...
handlers.py:
import webapp2
import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb
import webapp2_extras.appengine.auth.models as auth_models
from google.appengine.api import users
from webapp2_extras import sessions, auth # we'll use auth later on
from webapp2_extras.auth import InvalidAuthIdError
from webapp2_extras.auth import InvalidPasswordError
from wtforms import Form, TextField, PasswordField, validators
from webapp2_extras.appengine.users import login_required
def jinja2_factory(app):
j = jinja2.Jinja2(app)
j.environment.filters.update({
# Set filters. (http://tinyurl.com/jinja2-factory)
# ...
})
j.environment.globals.update({
# Set global variables.
'uri_for': webapp2.uri_for,
# ...
})
return j
def login_required(handler):
def check_login(self, *args, **kwargs):
if not self.user:
return self.redirect_to('login')
else:
return handler(self, *args, **kwargs)
return check_login
class UserAwareHandler(webapp2.RequestHandler):
def dispatch(self):
try:
super(UserAwareHandler, self).dispatch()
finally:
# Save the session after each request
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session_store(self):
return sessions.get_store(request=self.request)
#webapp2.cached_property
def session(self):
return self.session_store.get_session(backend="datastore")
#webapp2.cached_property
def auth(self):
return auth.get_auth(request=self.request)
#webapp2.cached_property
def user(self):
user = self.auth.get_user_by_session()
return user
#webapp2.cached_property
def user_model(self):
user_model, timestamp = self.auth.store.user_model.get_by_auth_token(
self.user['user_id'],
self.user['token']) if self.user else (None, None)
return user_model
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(factory=jinja2_factory, app=self.app)
def render_response(self, _template, **context):
ctx = {'user': self.user_model}
ctx.update(context)
rv = self.jinja2.render_template(_template, **ctx)
self.response.write(rv)
class SignupForm(Form):
email = TextField('Email',
[validators.Required(),
validators.Email()])
password = PasswordField('Password',
[validators.Required(),
validators.EqualTo('confirm_password',
message="Passwords must match.")])
password_confirm = PasswordField('Confirm Password',
[validators.Required()])
class SignupHandler(UserAwareHandler):
#Serves up a signup form, creates new users
def get(self):
self.render_response("templates/signup.html", form=SignupForm())
def post(self):
form = SignupForm(self.request.POST)
error = None
if form.validate():
success, info = self.auth.store.user_model.create_user(
"auth:" + form.email.data,
unique_properties=['email'],
email= form.password.data,
password_raw= form.password.data)
if success:
self.auth.get_user_by_password("auth:"+form.email.data,
form.password.data)
return self.redirect_to("index")
else:
error = "That email is already in use." if 'email'\
in user else "Something has gone horrible wrong."
self.render_response("templates/signup.html", form=form, error=error)
class LoginForm(Form):
email = TextField('Email',
[validators.Required(), validators.Email()])
password = PasswordField('Password',
[validators.Required()])
class LoginHandler(UserAwareHandler):
def get(self):
self.render_response("templates/index.html", form=LoginForm())
def post(self):
form = LoginForm(self.request.POST)
error = None
if form.validate():
try:
self.auth.get_user_by_password(
"auth:"+form.email.data,
form.password.data)
return self.redirect_to('secure')
except (auth.InvalidAuthIdError, auth.InvalidPasswordError):
error = "Invalid Email / Password"
self.render_response("templates/login.html", form=form, error=error)
class LogoutHandler(UserAwareHandler):
#Destroy the user session and return them to the login screen.
#login_required
def get(self):
self.auth.unset_session()
self.redirect_to('login')
class IndexHandler(UserAwareHandler):
def get(self):
ctx = {
'title1': "ALAZA",
'title2': "HOA",
'slogan': "A communication tool for the Alazan HOA.",
'message1': """
<p>The whole idea here is to show how to set up a simple static web site
on Google App Engine. I want to create an easy way to host your modest web
site on App Engine. My approach is dead simple. All I use is some boilerplate
code almost anyone can follow. You can have multiple pages and use template
variable features that are part of App Engine's WebApp Framework. Most modest
web sites don't do much more than this. Your certainly free to expand on what
you find here.</p>""",
}
self.render_response('templates/index.html', **ctx)
main.py:
import webapp2
import config
import routes
import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb
import webapp2_extras.appengine.auth.models as auth_models
class AwesomeUser(auth_models.User):
email = ndb.StringProperty()
webapp2_config = {}
webapp2_config['webapp2_extras.sessions'] = {
'secret_key': 'othello',}
webapp2_config['webapp2_extras.auth'] = {
'user_model': AwesomeUser,}
app = webapp2.WSGIApplication(config=config.webapp2_config)
routes.add_routes(app)
routes.py:
import handlers
import webapp2
from webapp2_extras.routes import RedirectRoute
# Using redirect route instead of simple routes since it supports strict_slash
# Simple route: http://webapp-improved.appspot.com/guide/routing.html#simple-routes
# RedirectRoute: http://webapp-improved.appspot.com/api/webapp2_extras/routes.html#webapp2_extras.routes.RedirectRoute
_routes = [
RedirectRoute('templates/login.html', handlers.LoginHandler, name='login'),
RedirectRoute('/templates/logout.html', handlers.LogoutHandler, name='logout'),
RedirectRoute('/templates/index.html', handlers.IndexHandler, name='index'),
RedirectRoute('/templates/signup.html', handlers.SignupHandler, name='signup'),]
def get_routes():
return _routes
def add_routes(app):
if app.debug:
secure_scheme = 'http'
for r in _routes:
app.router.add(r)
app.yaml:
application: alazan-hoa
version: main
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /js
static_dir: js
- url: /images
static_dir: images
- url: /css
static_dir: css
- url: /.*
script: main.app
libraries:
- name: webapp2
version: "2.5.1"
- name: jinja2
version: "2.6"
builtins:
- appstats: on
In your routes.py file, in the RedirectRoute you specify html files, while you should specify paths that are mapped to handler methods and inside them render the html templates.
For example, in your routes.py file you should have something like this:
_routes = [
RedirectRoute('/login', handlers.LoginHandler, name='login'),
RedirectRoute('/logout', handlers.LogoutHandler, name='logout'),
RedirectRoute('/index', handlers.IndexHandler, name='index'),
RedirectRoute('/signup', handlers.SignupHandler, name='signup')
]
You can take a look at Google App Engine Boilerplate for an introduction to Google App Engine development and best practices around it.
Hope this helps!
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
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.
I'm new to Google App Engine and Django forms. I'm getting an encoding error when I try to run the follow code:
import webapp2
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from django import newforms as forms
from google.appengine.ext.db import djangoforms
import os
import re
import fix_path
import config
import static
def slugify(s):
return re.sub('[^a-zA-Z0-9-]+', '-', s).strip('-')
def format_post_path(post, num):
slug = slugify(post.title)
if num > 0:
slug += "-" + str(num)
return config.post_path_format % {
'slug': slug,
'year': post.published.year,
'month': post.published.month,
'day': post.published.day,
}
def render_template(template_name, template_vals=None, theme=None):
template_path = os.path.join("themes", theme or config.theme, template_name)
return template.render(template_path, 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)
def publish(self):
rendered = self.render()
if not self.path:
num = 0
content = None
while not content:
path = format_post_path(self, num)
content = static.add(path, rendered, "text/html")
num += 1
self.path = path
self.put()
else:
static.set(self.path, rendered, "text/html")
class PostForm(djangoforms.ModelForm):
class Meta:
model = BlogPost
exclude = ['path', 'published', 'update']
class PostHandler(webapp2.RequestHandler)
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())
def post(self)
form = PostForm(date=self.request.POST)
if form.is_valid():
post = form.save(commit=False)
post.publish()
self.render_to_response("published.html", {'post': post})
else:
self.render_form(form)
app = webapp2.WSGIApplication([('/admin/newpost', PostHandler)],
debug=True)
Here is the traceback.
ERROR 2012-05-08 10:35:03,609 cgi.py:121] Traceback (most recent call last):
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver_import_hook.py", line 676, in Decorate
return func(self, *args, **kwargs)
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver_import_hook.py", line 1911, in get_code
source_code.decode(encoding)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 0: ordinal not in range(128)
INFO 2012-05-08 10:35:03,627 dev_appserver.py:2891] "GET /admin/newpost HTTP/1.1" 500 -
The traceback doesn't give me enough information to fix the problem. Any ideas?
When I had this problem with appengine I could clean all my data to unicode / utf-8 and then it worked.
Some of your data is not unicode / utf-8. I've had this problem on several occassions with GAE and it's always the data and not the source code that is not unicode in some way.
If you inspect your data and/or source formatting and set everything to utf-8 / unicode it will work. It might seem that you've already set everything to utf-8, but if you're still getting this error it means some string or the like is is some other charset than utf-8.