Подтвердить что ты не робот

Папка шаблона шаблона флага

Мой макет приложения для флэков:

myapp/
    run.py
    admin/
        __init__.py
        views.py
        pages/
            index.html
    main/
        __init__.py
        views.py
        pages/
            index.html

_ init _.py файлы пустые. admin/views.py:

from flask import Blueprint, render_template
admin = Blueprint('admin', __name__, template_folder='pages')

@admin.route('/')
def index():
    return render_template('index.html')

main/views.py похож на admin/views.py:

from flask import Blueprint, render_template
main = Blueprint('main', __name__, template_folder='pages')

@main.route('/')
def index():
    return render_template('index.html')

run.py:

from flask import Flask
from admin.views import admin
from main.views import main

app = Flask(__name__)
app.register_blueprint(admin, url_prefix='/admin')
app.register_blueprint(main, url_prefix='/main')

print app.url_map

app.run()

Теперь, если я получаю доступ к http://127.0.0.1:5000/admin/, он корректно отображает admin/index.html. Тем не менее, http://127.0.0.1:5000/main/ показывает еще admin/index.html вместо main/index.html. Я проверил app.url_map:

<Rule 'admin' (HEAD, OPTIONS, GET) -> admin.index,
<Rule 'main' (HEAD, OPTIONS, GET) -> main.index,

Кроме того, я проверил, что функция index в main/views.py вызывается как ожидалось. Если я переименую main/index.html на что-то другое, то оно будет работать. Таким образом, без переименование, как можно добиться этого 1http://127.0.0.1: 5000/main/1 показывает main/index.html?

4b9b3361

Ответ 1

Как и в случае с флягой 0.8, чертежи добавляют указанную template_folder в путь поиска приложения, а не обрабатывают каждый из каталогов как отдельные объекты. Это означает, что если у вас есть два шаблона с одинаковым именем файла, первый из найденных в пути поиска - тот, который используется. Это, по общему признанию, запутанно и плохо документировано в это время (см. эта ошибка). Похоже, что вы не были единственным, кто был смущен этим поведением.

Причина создания такого поведения заключается в том, что шаблоны чертежей можно легко переоценить из основных шаблонов приложений, которые являются первыми в строке в пути поиска шаблона Flask.

Приходят на ум два варианта.

  • Переименуйте каждый из файлов index.html как уникальный (например, admin.html и main.html).
  • В каждой папке шаблона поместите каждую из шаблонов в подкаталоге папки чертежа, а затем вызвать шаблон с использованием этого подкаталога. Например, ваш шаблон администратора будет yourapp/admin/pages/admin/index.html, а затем вызван изнутри план как render_template('admin/index.html').

Ответ 2

В дополнение к хорошим предложениям linqq выше, вы также можете переопределить функциональность по умолчанию, если это необходимо. Есть несколько способов:

Можно переопределить create_global_jinja_loader в подклассовом приложении Flask (которое возвращает DispatchingJinjaLoader, заданное в файле flask/templating.py). Это не рекомендуется, но будет работать. Причина, по которой это обескураживается, заключается в том, что DispatchingJinjaLoader обладает достаточной гибкостью для поддержки инъекции пользовательских загрузчиков. И если вы закрутите свой собственный загрузчик, он сможет опираться на стандартную функциональность.

Итак, рекомендуется, чтобы вместо этого был "переопределить функцию jinja_loader". В этом случае отсутствует документация. Стратегия загрузки Patching Flask требует некоторых знаний, которые, как представляется, не документированы, а также хорошее понимание Jinja2.

Есть два компонента, которые вам нужно понять:

  • Среда Jinja2
  • Загрузчик шаблонов Jinja2

Они создаются Flask с разумными значениями по умолчанию автоматически. (Вы можете указать свои собственные параметры Jinja2, кстати, переопределив app.jinja_options - но имейте в виду, что вы потеряете два расширения, которые по умолчанию включает флажок - autoescape и with - если вы не указали их сами. Посмотрите на флягу /app.py, чтобы посмотреть, как они ссылаются на них.)

В среде содержатся все эти процессоры контекста (например, вы можете сделать var|tojson в шаблоне), вспомогательные функции (url_for и т.д.) и переменные (g, session, app), Он также содержит ссылку на загрузчик шаблонов, в этом случае вышеназванный и автоматически созданный DispatchingJinjaLoader. Поэтому, когда вы вызываете render_template в своем приложении, он находит или создает среду Jinja2, устанавливает все эти свойства и вызывает на ней get_template, который в свою очередь вызывает get_source внутри DispatchingJinjaLoader, который пытается несколько стратегий, описанных ниже.

Если все идет по плану, эта цепочка решит найти файл и вернет его содержимое (и некоторые другие данные). Также обратите внимание, что это тот же путь выполнения, что и {% extend 'foo.htm' %}.

DispatchingJinjaLoader выполняет две вещи: сначала он проверяет, может ли глобальный загрузчик приложения, который app.jinja_loader, найти файл. В противном случае он проверяет все чертежи приложений (в порядке регистрации, AFAIK) для blueprint.jinja_loader в попытке найти файл. Трассировка этой цепочки до самого конца, вот определение jinja_loader (в flask/helpers.py, _PackageBoundObject, базовый класс как приложения Flask, так и чертежей):

def jinja_loader(self):
    """The Jinja loader for this package bound object.

    .. versionadded:: 0.5
    """
    if self.template_folder is not None:
        return FileSystemLoader(os.path.join(self.root_path,
                                             self.template_folder))

Ах! Итак, теперь мы видим. Очевидно, что пространства имен обоих конфликтов будут конфликтуют с теми же именами каталогов. Поскольку глобальный загрузчик вызывается первым, он всегда будет выигрывать. (FileSystemLoader является одним из нескольких стандартных загрузчиков Jinja2.) Однако это означает, что нет действительно простого способа изменить порядок использования Blueprint и загрузчика шаблонов приложений.

Итак, нам нужно изменить поведение DispatchingJinjaLoader. Некоторое время я думал, что нет хорошего, не обескураженного и эффективного способа обойти это. Однако, по-видимому, если вы переопределите app.jinja_options['loader'], мы сможем получить нужное поведение. Итак, если мы подклассом DispatchingJinjaLoader и модифицируем одну небольшую функцию (я полагаю, что было бы лучше полностью переопределить ее, но это работает пока), мы хотим, чтобы мы поступили. В общей сложности разумной стратегией будет следующая (непроверенная, но должна работать с современными приложениями Flask):

from flask.templating import DispatchingJinjaLoader
from flask.globals import _request_ctx_stack

class ModifiedLoader(DispatchingJinjaLoader):
    def _iter_loaders(self, template):
        bp = _request_ctx_stack.top.request.blueprint
        if bp is not None and bp in self.app.blueprints:
            loader = self.app.blueprints[bp].jinja_loader
            if loader is not None:
                yield loader, template

        loader = self.app.jinja_loader
        if loader is not None:
            yield loader, template

Это изменяет стратегию исходного загрузчика двумя способами: сначала попытайтесь загрузить из чертежа (и ТОЛЬКО исполняемый проект, а не все чертежи), и если это не удается, загрузите только приложение. Если вам нравится поведение всего плана, вы можете сделать несколько копий-пасты из фляги /templating.py.

Чтобы связать все это вместе, вы должны установить jinja_options в объект Flask:

app = Flask(__name__)
# jinja_options is an ImmutableDict, so we have to do this song and dance
app.jinja_options = Flask.jinja_options.copy() 
app.jinja_options['loader'] = ModifiedLoader(app)

В первый раз, когда требуется среда шаблона (и, таким образом, создается экземпляр), то есть при первом вызове render_template, ваш загрузчик должен использоваться.

Ответ 3

twooster ответ интересен, но другая проблема заключается в том, что Jinja по умолчанию кэширует шаблон на основе его имени. Поскольку оба шаблона называются "index.html", загрузчик не будет запускаться для последующих чертежей.

Помимо linqq два предложения, третий вариант заключается в том, чтобы игнорировать параметр шаблонов шаблона_памят вместе и разместить шаблоны в соответствующих папках в каталоге шаблонов приложений.

т

myapp/templates/admin/index.html
myapp/templates/main/index.html

Ответ 4

Я использую что-то подобное в fypress и fybb, потому что у меня есть система тем.

# utils.templates
from jinja2 import Environment, PackageLoader
from flask.templating import _default_template_ctx_processor
from flask import current_app, url_for, get_flashed_messages


admin_env = Environment(
    loader=PackageLoader('fypress', '/templates/admin/'),
    extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'],
    autoescape=True
)

def render_template(template, **kwargs):
    kwargs.update(_default_template_ctx_processor())
    kwargs.update({
        'url_for': url_for,
        'get_flashed_messages': get_flashed_messages # etc...

    })
    kwargs.update(dict(debug=current_app.config.get('DEBUG'), flask_config=current_app.config))

    template = admin_env.get_template(template)
    return template.render(**kwargs)

А потом

# routes.admin.
from flask import Blueprint
from utils.templates import render_template

admin_bp = Blueprint('admin', __name__,  url_prefix='/admin')

@admin_bp.route('/')
def root():
    return render_template('index.html', title='Admin')

Ответ 5

Tks @linqq, ваш метод действительно хорошо работает здесь, кроме того, я сделал лучшее решение от декоратора.

Внимание, не импортируйте функцию render_template следующим образом:

from flask import render_template

Вы должны импортировать модуль фляги следующим образом:

import flask

Затем создайте этот блок кода в верхней части файла маршрутизатора:

def render_decorate(path_prefix):
    def decorate(func):
        def dec_func(*args, **kw):
            arg_list = list(args)
            arg_list[0] = path_prefix + str(arg_list[0])
            arg_tuple = tuple(arg_list)
            return func(*arg_tuple, **kw)
        return dec_func
    return decorate

@render_decorate("%YOUR_DIRECTORY_NAME%/")
def render_template(template_name_or_list, **context):
    return flask.render_template(template_name_or_list, **context)

Замените% YOUR_DIRECTORY_NAME% своим фактическим путем и убедитесь, что ваша папка шаблонов выглядит следующим образом: введите описание изображения здесь И все готово! Просто используйте функцию render_template как обычно.