TemplateDoesNotExist на python app-engine django 1.2 при шаблонном рендеринге относительных путей - PullRequest
11 голосов
/ 10 марта 2011

Я использую 1.4.2 appengine SDK локально на машине с Windows.У меня есть приложение под управлением Django 0.96.Для рендеринга шаблонов используется оболочка django из

google.appengine.ext.webapp.template.render

.Я часто использую относительный путь для связывания своих шаблонов, например

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

После обновления до Django 1.2 метод find_template из django.template.loader в папке lib приложения Django 1.2 lib теперь вызывает 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, но не вижу никакой причины для этого.Кто-нибудь может предоставить больше понимания?

Спасибо,

Ричард

Ответы [ 2 ]

15 голосов
/ 10 марта 2011

Эта проблема меня тоже беспокоила, когда я конвертировал из 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.Конечным результатом является то, что

{% 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.

0 голосов
/ 12 января 2012

Одна из причин, по которой вы получаете сообщение об ошибке TemplateDoesNotExist: templates \ AdminListstr.html

Дважды проверьте слэш, который вы используете между "шаблонами" и именем вашего шаблона. Я потратил впустую несколько часов, затем проверил значение, возвращаемое os.path.join.

Итак, вам нужно убедиться, что вы используете "/", а не Windows "\". Если вы используете неправильную косую черту, ваш код будет работать при разработке (Windows), но при развертывании не будет работать.

Мой диагностический код напечатал это: посмотрите на последнюю косую черту И возвращаемое os.path.join значение: /base/data/home/apps/s~myapp/1.356037954289984934/templates \ LabListstr.html

Да, йи, йи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...