TemplateDoesNotExist在python应用程序引擎django 1.2中,同时模板呈现相对路径 [英] TemplateDoesNotExist on python app-engine django 1.2 while template rendering relative paths

查看:114
本文介绍了TemplateDoesNotExist在python应用程序引擎django 1.2中,同时模板呈现相对路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Windows计算机上本地运行1.4.2 appengine SDK。我有一个运行Django 0.96的应用程序。模板渲染使用来自

  google.appengine.ext.webapp.template.render 
的django包装器。 code>

来渲染模板。我经常使用相对路径来链接我的模板,例如

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

升级到Django 1.2之后,appengine的
django.template.loader中的find_template方法在使用相对路径时,Django 1.2 lib文件夹现在引发了TemplateDoesNotExist对于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,名称,目录))
除TemplateDoesNotExist外:
通过
提升TemplateDoesNotExist(名称)

现在我一直在浏览Django和AppEngine代码,但看不到任何理由。任何人都可以提供更多见解吗?



谢谢,

理查

解决方案

当我从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 )。它不会允许相对路径超出它认为的顶层目录。这与配置Web服务器的功能相同,只需将 .. 添加到您的URL中即可阻止您访问服务器的整个文件系统。最终的结果是,

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

过去一直很好打破了规则,它不会起作用。



在我的应用程序中解决这个问题的方式没有完全重构模板的布局方式,而是通过实现一个自定义的 TemplateLoader 。 Django的模板渲染引擎允许应用程序拥有许多不同的类,以了解如何以不同方式查找模板。如果您查看上面给出的目录,您会看到有几个提供的,它们都是从 BaseLoader 继承的类。我提供了我自己的定制的模板布局。



我的项目有类似Rails的布局:

 应用程序/ 
控制器/
home_controller.py
posts_controller.py
模型/
。 ..
views /
主页/
index.html
about.html
帖子/
show.html
new.html
共享/
base.html
post.html

每个模板都会扩展 base.html 和一对包括 post.html ,他们以前使用相对路径到达碱/ 。理想情况下,我甚至不想使用 .. up-dir来达到目标​​,但它需要0.96。我创建了以下模板加载器来处理我的方案:

 来自django.conf导入设置
来自django.template从django.template.loader导入TemplateDoesNotExist
从django.utils._os导入BaseLoader
导入safe_join
导入os

class MvcTemplateLoader(BaseLoader):
MVCEngine框架的自定义模板加载器。

is_usable = True

__view_paths = None

def __init __(self,views_path):
self.views_path = views_path
#我们只需要实例化view_paths类变量一次。
如果MvcTemplateLoader .__ view_paths是None:
temp_paths = []
for os_listdir(views_path)中的each_path:
#我们想要跳过隐藏的目录,所以避免任何开始与。
#这对Windows和* NIX都适用,但对其他操作系统可能会失败吗?
if not each_path.startswith('。'):
full_path = os.path.join(views_path,each_path)
if each_path ==shared:
#共享目录是特殊的。由于许多其他目录中的模板将是
#,它们继承自或包含模板,所以它应该紧接在
#root目录之后。现在,它将是第一个。
temp_paths.insert(0,full_path)
else:
temp_paths.append(full_path)
#为了给模板赋予决议优先级,根views_path本身总是第一个由父目录指定的b $ b#。换句话说,home / index.html将立即解析
#而没有歧义;而index.html可以解析为bar / index.html而不是
#foo / index.html。
temp_paths.insert(0,views_path)
MvcTemplateLoader .__ view_paths = temp_paths

$ b def get_template_sources(self,template_name):
用于MvcTemplateLoader中的template_dir。 __view_paths:
try:
产生safe_join(template_dir,template_name)
,但UnicodeDecodeError:
#模板目录名是一个无效的UTF-8字节串。
除了ValueError之外,还有

#加入的路径位于这个特定的
#template_dir之外(它可能在另一个之内,所以这不是
#致命)。
传递

在self.get_template_sources(template_name)中为filepath指定load_template_source(self,template_name,template_dirs = None):
Try = []

试试:
file = open(filepath)
try:
return(file.read()。decode(settings.FILE_CHARSET),filepath)
finally:
file.close()
除了IOError:
try.append(文件路径)
$ b error_msg =在任何视图子目录中找不到%s。 %template_name
raise TemplateDoesNotExist(error_msg)
load_template_source.is_usable = True

_loader = MvcTemplateLoader

我通过将我的应用程序的 main 函数更改为Django尝试的集合,使我的自定义模板加载器包含在该集合中:

  def main():
from google.appengine.dist import use_library
use_library('django' ,'1.2')

os.environ ['DJANGO_SETTINGS_MODULE'] ='设置'

从django.conf导入设置
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')

(然后所有其余的通常是goe的东西s到你的主函数中)。

所以,我认为你应该能够修改上面的TemplateLoader代码来匹配你如何布置模板目录,不仅可以更好地控制你如何布置模板层次结构,而且可以更好地控制你如何编写 extends include 声明。你不再使用 .. ,而只是给出模板的路径相对于你项目中的任何东西等于我的 views 目录。


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 extends and include commands. 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_join to join path parts (you can see the code in google_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 the

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

that 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:

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

Every template extends base.html and a couple include post.html, and they previously used relative paths to get to their location in base/. 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:

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

And I caused my custom template loader to be included in the collection that Django tries by changing my application's main function to look like this:

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

(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 extends and include statement. You no longer use .. but rather just give the path of the template relative to whatever in your project is the equivalent of my views directory.

这篇关于TemplateDoesNotExist在python应用程序引擎django 1.2中,同时模板呈现相对路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆