I’m running the 1.4.2 appengine SDK locally on a windows machine. I have an application running Django 0.96. The template rendering is using the django wrapper from
google.appengine.ext.webapp.template.render
to render templates. I often use a relative path to link my templates e.g
{% extends "../templates/base.html" %}
After upgrading to Django 1.2 the find_template method from
django.template.loader in the appengine’s Django 1.2 lib folder is now raising a TemplateDoesNotExist when the relative paths are used
for loader in template_source_loaders:
try:
#raises TemplateDoesNotExist name='../templates/home.html' dirs=None
source, display_name = loader(name, dirs)
return (source, make_origin(display_name, loader, name, dirs))
except TemplateDoesNotExist:
pass
raise TemplateDoesNotExist(name)
I’ve been stepping through the Django and AppEngine code for a while now but can’t see any reason for this. Can anyone provide any more insight?
Thanks,
Richard
This problem bit me too when I converted from 0.96 to 1.2 Django templates. I was initially pushed to do so when SDK 1.4.2 started issuing the warning that I needed to pick a version, but when I looked into the much-needed improvements in the template language, I was eager to make the change.
And then everything broke. Like you, I used a lot of relative paths in my
extendsandincludecommands. It took a lot of debugging and digging, but I did figure out the cause of the problem and a pretty good solution.The cause: in Django 1.2, the code that loads template files started using a command called
safe_jointo join path parts (you can see the code ingoogle_appengine\lib\django_1_2\django\template\loaders\filesystem.py) . It won’t allow relative paths to go above what it thinks of as the top-level directory. This is the same thing as a web server being configured to prevent you gaining access to the server’s whole filesystem just by sticking some..‘s into your URL. The end result is that thethat used to be just fine breaks the rules and it isn’t going to work.
The way that I fixed this in my application without completely restructuring how my templates are laid out is by implementing a custom TemplateLoader. Django’s template rendering engine allows an application to have many different classes that know how to find templates in different ways. If you look in the directory that I gave above, you’ll see that there are several provided, and they are all classes that inherit from BaseLoader. I provided my own that is custom-tailored to how my templates are laid out.
My project has a Rails-like lay-out:
Every template extends
base.htmland a couple includepost.html, and they previously used relative paths to get to their location inbase/. Ideally, I didn’t even want to use the..up-dir to get there, but it was required with 0.96. I created the following template loader to work with my scheme:And I caused my custom template loader to be included in the collection that Django tries by changing my application’s
mainfunction to look like this:(and then all the rest of the stuff that normally goes into your main function).
So, I think that you should be able to modify the TemplateLoader code above to match how you have your template directories laid out, and it will give you a greater control over not only how you layout you templates hierarcy but also how you write your
extendsandincludestatement. You no longer use..but rather just give the path of the template relative to whatever in your project is the equivalent of myviewsdirectory.