我在Windows机器上本地运行1.4.2 appengine SDK。我有一个运行Django 0.96的应用程序。模板渲染使用
中的django包装器google.appengine.ext.webapp.template.render
渲染模板。我经常使用相对路径链接我的模板,例如
{% extends "../templates/base.html" %}
从Django 1.2升级到find_template方法之后 在使用相对路径时,appengine的Django 1.2 lib文件夹中的django.template.loader现在正在引发TemplateDoesNotExist
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)
我一直在逐步浏览Django和AppEngine代码,但看不出任何理由。任何人都可以提供更多见解吗?
谢谢,
理查德
答案 0 :(得分:15)
当我从0.96转换为1.2 Django模板时,这个问题也让我感到困惑。当SDK 1.4.2开始发出我需要选择版本的警告时,我最初被推动这样做,但当我研究模板语言急需的改进时,我急于做出改变。
然后一切都破了。和你一样,我在extends
和include
命令中使用了很多相对路径。它花了很多调试和挖掘,但我确实找出了问题的原因和一个非常好的解决方案。
原因:在Django 1.2中,加载模板文件的代码使用名为safe_join
的命令开始加入路径部分(您可以在google_appengine\lib\django_1_2\django\template\loaders\filesystem.py
中看到代码)。它不允许相对路径超出它认为的顶级目录。这与配置为阻止您通过将一些..
粘贴到您的URL中来访问服务器的整个文件系统的Web服务器相同。
{% extends "../templates/base.html" %}
曾经很好地打破了规则而且不会起作用。
我在应用程序中修复此问题的方法是在不完全重构模板布局的情况下实现自定义 TemplateLoader 。 Django的模板渲染引擎允许应用程序拥有许多不同的类,这些类知道如何以不同的方式查找模板。如果您查看我上面给出的目录,您会看到提供了几个,并且它们都是从 BaseLoader 继承的类。我提供了自己定制的自定义我的模板的方式。
我的项目有类似Rails的布局:
app/
controllers/
home_controller.py
posts_controller.py
models/
...
views/
home/
index.html
about.html
posts/
show.html
new.html
shared/
base.html
post.html
每个模板都展开base.html
,其中一对包含post.html
,之前他们使用相对路径到达base/
中的位置。理想情况下,我甚至不想使用..
up-dir到达那里,但它需要0.96。我创建了以下模板加载器来使用我的方案:
from django.conf import settings
from django.template import TemplateDoesNotExist
from django.template.loader import BaseLoader
from django.utils._os import safe_join
import os
class MvcTemplateLoader(BaseLoader):
"A custom template loader for the MVCEngine framework."
is_usable = True
__view_paths = None
def __init__(self, views_path):
self.views_path = views_path
# We only need to instantiate the view_paths class variable once.
if MvcTemplateLoader.__view_paths is None:
temp_paths = []
for each_path in os.listdir(views_path):
# We want to skip hidden directories, so avoid anything that starts with .
# This works on both Windows and *NIX, but could it fail for other OS's?
if not each_path.startswith('.'):
full_path = os.path.join(views_path, each_path)
if each_path == "shared":
# The shared directory is special. Since templates in many other directories will be
# inheriting from or including templates there, it should come second, right after the
# root views directory. For now, it will be first.
temp_paths.insert(0, full_path)
else:
temp_paths.append(full_path)
# The root views_path itself will always be first in order to give resolution precendence to templates
# that are specified with a parent directory. In other words, home/index.html will be immediately
# resolved with no ambiguity; whereas, index.html could resolve as bar/index.html rather than
# foo/index.html.
temp_paths.insert(0, views_path)
MvcTemplateLoader.__view_paths = temp_paths
def get_template_sources(self, template_name):
for template_dir in MvcTemplateLoader.__view_paths:
try:
yield safe_join(template_dir, template_name)
except UnicodeDecodeError:
# The template dir name was a bytestring that wasn't valid UTF-8.
raise
except ValueError:
# The joined path was located outside of this particular
# template_dir (it might be inside another one, so this isn't
# fatal).
pass
def load_template_source(self, template_name, template_dirs=None):
tried = []
for filepath in self.get_template_sources(template_name):
try:
file = open(filepath)
try:
return (file.read().decode(settings.FILE_CHARSET), filepath)
finally:
file.close()
except IOError:
tried.append(filepath)
error_msg = "Could not find %s in any of the views subdirectories." % template_name
raise TemplateDoesNotExist(error_msg)
load_template_source.is_usable = True
_loader = MvcTemplateLoader
我将自定义模板加载器包含在Django尝试的集合中,方法是将应用程序的main
函数更改为:
def main():
from google.appengine.dist import use_library
use_library('django', '1.2')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.conf import settings
views_path = os.path.join(os.path.dirname(__file__), 'app','views')
settings.TEMPLATE_LOADERS = (('gaemvclib.mvctemplateloader.MvcTemplateLoader', views_path), 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader')
(然后通常进入你的主要功能的所有其他东西)。
所以,我认为您应该能够修改上面的TemplateLoader代码以匹配模板目录的布局,并且它不仅可以让您更好地控制模板层次布局的方式,还可以更好地控制写下您的extends
和include
声明。您不再使用..
,而只是提供模板的路径,相对于项目中的任何内容,相当于我的views
目录。
答案 1 :(得分:-2)
收到错误消息的一个原因 TemplateDoesNotExist: templates \ AdminListstr.html
仔细检查"模板"之间使用的斜杠和你的模板名称。我浪费了几个小时,然后检查了os.path.join返回的值。
总之,您需要确保使用" /"不是Windows" \"。如果使用错误的斜杠,则代码将在(Windows)开发中起作用,但在部署时会失败。
我的诊断代码打印了这个:看最后一个斜线 os.path.join返回的值为:/ base/data/home/apps/s~myapp/1.356037954289984934/templates \ LabListstr.html
Aye yi yi!