首页 文章

Flask模板包括不正确的文件

提问于
浏览
2

我在Flask中使用 jinja2.ChoiceLoader (也尝试使用带有 FileSystemLoader 的多路径,没有欢乐)时遇到了一个相当奇怪的问题 .

我有几个“主题”目录,就像这样 .

/templates/
  themes/
    default/
      layout.html
      menu.html
    blue/
      layout.html
    grey/
      menu.html
    ...

如果所选主题没有所需的模板,我想回退到 default/ ,所以我使用了ChoiceLoader,就像这样 .

@app.before_request
def setup_request():
    current_theme = get_theme()
    logging.info('Using theme %s'%(current_theme))
    app.jinja_loader = jinja2.ChoiceLoader([
        jinja2.FileSystemLoader('/templates/themes/%s/'%(current_theme)),
        jinja2.FileSystemLoader('/templates/themes/default/')
    ])

这很好,但是如果我更改 <current_theme> 它仍会从旧文件夹加载主题,直到我重新加载Apache或重新启动Flask开发服务器 .

应该使用新主题 . 记录显示它正在使用已更改的主题,但显然 app.jinja_loader 有点像蜜獾......在重新加载Apache之前,它完全忽略了它 .

Edit: 这似乎与Flask有关,因为所有同名文件都是同一个文件 . 我可以使用内置服务器(使用DEBUG = True),Cherry和mod_wsgi进行重现 . 这个人似乎有类似的问题,但没有简单的解决方案:flask blueprint template folder我的情况不同,我需要为单个应用程序的级联模板 . 他的问题与蓝图之间的级联模板有关,但它可能是同样的问题 .

这是“get_theme()”调用中的代码:

def get_theme():
    # I know this is unsafe, testing only
    return request.args.get('theme','default')

Edit 2: 我需要更改主题之间的HTML和JavaScript,而不仅仅是CSS . 这就是为什么我不只是加载不同的CSS文件 . 此外,其中一些主题适用于移动设备,与其他主题几乎没有共同之处 .

Edit 3: 两种解决方案 . 解决方案1:唯一地命名文件,如"blue.layout.html"和"default.layout.html" . 这完美地工作,但它不按要求级联 . 解决方案2:使用相对路径,因此使用 include 'theme/blue/file.html 而不是 include 'file.html' . 我通过创建一个检查活动主题的 get_theme_file() 函数来实现级联,检查文件是否存在(如果不存在,回退到"default"主题),并返回相对路径 . 我只需要确保我包含的所有内容看起来像 {% include get_theme_file('file.html') %} . 这不是很优雅,但是我发现使用Flask的低级摆弄更加优雅here .

2 回答

  • 0

    顺便说一句,您可以将多个位置传递给FileSystemLoader,这是加载模板的推荐方法

    这是Apache中的预期行为 mod_wsgi (我假设您正在使用) . 文件系统更改不会触发所有进程的重新加载 . 请参阅Flask文档中的this entry讨论此问题,并提供一个解决方法,即添加:

    WSGIScriptReloading On

    到应用程序的配置部分,然后 touchwsgi 文件以触发重新加载子进程 .

    你确定这是你想要的吗?大多数主题切换技巧依赖于层叠样式表(CSS)的级联部分来控制主题 .

  • 0

    好吧,我不是唯一一个遇到这个问题的人 . 问题是Flask基于文件名缓存,如果你没有包含相对路径,它只会缓存第一个要加载的路径 . 有三种方法可以完成动态,级联的模板 .

    • Override Jinja builtins . 我发现这很令人困惑 . 我对这个解决方案不够聪明 .

    • 为每个文件提供不同的WSGI进程 . 对于动态网站来说,这里的设置似乎有点太多了 . Flask为每个WSGI进程缓存文件名,因此您可以使用多个Cherry WSGI服务器执行某些操作 .

    • 在加载路径中包含主题 . 创建一个函数,使用该函数加载 context_processoronly 加载模板文件 . 该函数需要检查非默认主题,检查文件是否存在,并返回它 . 所以模板调用将是 get_theme_file('layout.html') ,它将返回相对路径(如 themes/blue/layout.html ) .

    选项3的一个例子 .

    def get_theme_file(fname):
        theme = get_theme()
        if os.path.exists(os.path.join(theme.theme_dir, fname)):
            return os.path.join('themes', theme.name, fname)
        return os.path.join('themes', 'default', fname)
    ...
        # Each render_template should reference this
        return render_template(get_theme_file('layout.html'))
    

    如果在模板中包含主题文件:

    {% include get_theme_file('layout.html') %}
    

    不幸的是,这不会缓存,但我可以看到一些方法来优化它 . 也许缓存 get_theme().theme_diros.listdir ,而不是为每个 get_theme_file 调用读取文件系统,只需对缓存的 listdir 列表进行 in 检查 .

    值得注意的是,这是Flask特有的 . 我无法使用普通的Jinja2和我自己的WSGI服务器重现这种行为 . 有人可能会说Flask对于这个特定项目来说是一个糟糕的选择,但我认为Flask所做的其他事情的节省是值得的 .

相关问题