首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >烧瓶蓝图模板文件夹

烧瓶蓝图模板文件夹
EN

Stack Overflow用户
提问于 2011-11-02 01:13:09
回答 7查看 46.6K关注 0票数 57

我的烧瓶应用程序布局如下:

代码语言:javascript
复制
myapp/
    run.py
    admin/
        __init__.py
        views.py
        pages/
            index.html
    main/
        __init__.py
        views.py
        pages/
            index.html

_init_.py文件是空的。admin/views.py内容是:

代码语言:javascript
复制
from flask import Blueprint, render_template
admin = Blueprint('admin', __name__, template_folder='pages')

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

to admin/views.py类似于main/views.py

代码语言:javascript
复制
from flask import Blueprint, render_template
main = Blueprint('main', __name__, template_folder='pages')

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

run.py是:

代码语言:javascript
复制
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:

代码语言:javascript
复制
<Rule 'admin' (HEAD, OPTIONS, GET) -> admin.index,
<Rule 'main' (HEAD, OPTIONS, GET) -> main.index,

此外,我还验证了main/views.py中的index函数是否按预期调用。如果我将main/index.html重命名为不同的名称,那么它可以工作。因此,如果不进行重命名,如何实现1http://127.0.0.1:5000/main/1显示main/index.html?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2011-11-19 23:56:04

从Flask0.8开始,蓝图将指定的template_folder添加到应用程序的搜索路径中,而不是将每个目录作为单独的实体处理。这意味着如果您有两个具有相同文件名的模板,那么在搜索路径中找到的第一个模板就是使用的模板。诚然,这是令人困惑的,而且此时的文档记录也不充分(请参阅这只虫子)。看起来说你不是唯一一个被这种行为搞糊涂的人。

这种行为的设计原因是因为蓝图模板很容易从主应用程序的模板中被覆盖,这些模板是Flask模板搜索路径中的第一行。

我想到了两个选择。

  • 将每个index.html文件重命名为唯一文件(例如,admin.htmlmain.html)。
  • 在每个模板文件夹中,将每个模板放在蓝图文件夹的子目录中,然后使用该子目录调用模板。例如,您的管理模板将是yourapp/admin/pages/admin/index.html,然后从蓝图中作为render_template('admin/index.html')调用。
票数 77
EN

Stack Overflow用户

发布于 2012-01-05 04:09:06

除了上述linqq的好建议外,如果需要,还可以覆盖默认功能。有几种方法:

可以在子类create_global_jinja_loader应用程序中重写DispatchingJinjaLoader (它返回在烧瓶/templating.py中定义的DispatchingJinjaLoader)。这是不推荐的,但会有效果。之所以不鼓励这样做,是因为DispatchingJinjaLoader具有足够的灵活性来支持自定义加载器的注入。如果你搞砸了你自己的装载机,它将能够依靠默认的,理智的功能。

因此,建议的是“重写jinja_loader函数”。这就是缺乏文档的原因。修补水瓶的加载策略需要一些似乎没有文档化的知识,以及对Jinja2的良好理解。

有两个组件您需要理解:

  • Jinja2环境
  • Jinja2模板加载程序

这些都是由Flask自动创建的,具有合理的缺省值。(顺便说一句,您可以通过重写Jinja2选项来指定您自己的app.jinja_options --但是请记住,您将失去两个默认包含的扩展-- autoescapewith --除非您自己指定它们。看看烧瓶/app.py,看看它们是如何引用这些的。)

环境包含所有这些上下文处理器(例如,您可以在模板中执行var|tojson )、辅助函数(url_for等)和变量(gsessionapp)。它还包含对模板加载器的引用,在本例中是上述和自动实例化的DispatchingJinjaLoader。因此,当您在应用程序中调用render_template时,它会找到或创建Jinja2环境,设置所有这些好东西,并在其上调用get_template,然后调用DispatchingJinjaLoader内部的get_source,后者尝试了后面描述的一些策略。

如果一切按计划进行,该链将解决查找文件时的问题,并返回其内容(和一些其他数据)。另外,请注意,这是与{% extend 'foo.htm' %}相同的执行路径。

DispatchingJinjaLoader做了两件事:第一,它检查应用程序的全局加载程序(即app.jinja_loader )是否可以定位文件。否则,它将检查blueprint.jinja_loader的所有应用程序蓝图(按注册顺序,AFAIK),试图定位该文件。跟踪这条链到最后,下面是jinja_loader的定义(在烧瓶/helpers.py中,_PackageBoundObject是Flask应用程序和蓝图的基类):

代码语言:javascript
复制
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加载器之一。)然而,这意味着没有真正简单的方法来重新排序蓝图和应用程序范围的模板加载器。

因此,我们需要修改DispatchingJinjaLoader的行为。有一段时间,我认为没有一个好的,没有气馁和有效的方式来做这件事。然而,显然,如果您覆盖app.jinja_options['loader']本身,我们可以得到我们想要的行为。因此,如果我们子类DispatchingJinjaLoader,并修改一个小函数(我想完全重新实现它可能会更好,但目前这是可行的),我们有我们想要的行为。总之,一个合理的战略是以下几点(未经测试,但应适用于现代的水瓶应用程序):

代码语言:javascript
复制
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

这将以两种方式修改原始加载程序的策略:首先尝试从蓝图加载(只加载当前正在执行的蓝图,而不是所有蓝图),如果失败,只能从应用程序加载。如果你喜欢全蓝图的行为,你可以做一些复制意大利面从烧瓶/模板. do。

要将所有这些连接在一起,您必须在Flask对象上设置jinja_options

代码语言:javascript
复制
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时,应该使用加载程序。

票数 24
EN

Stack Overflow用户

发布于 2012-06-21 19:34:42

twooster的答案很有趣,但另一个问题是Jinja默认情况下会缓存基于其名称的模板。因为这两个模板都被命名为"index.html",所以加载程序不会运行后续的蓝图。

除了linqq的两个建议之外,第三个选项是忽略蓝图的templates_folder选项,并将模板放在应用程序模板目录的各个文件夹中。

ie:

代码语言:javascript
复制
myapp/templates/admin/index.html
myapp/templates/main/index.html
票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7974771

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档