2

我在 Flask 中使用 a 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 之前,它完全忽略了它。

编辑:考虑到所有同名文件都是同一个文件,这似乎与 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')

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

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

4

2 回答 2

0

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

这是 Apache 中的预期行为mod_wsgi(我假设您正在使用)。文件系统更改不会触发所有进程的重新加载。请参阅Flask 文档中有关此内容的此条目,并提供一种解决方法,即添加

WSGIScriptReloading On

到应用程序的配置部分,然后touch输入wsgi文件以触发子进程的重新加载。

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

于 2013-04-14T04:41:00.340 回答
0

好吧,我不是唯一遇到这个问题的人。问题是 Flask 基于文件名进行缓存,如果您不包含相对路径,它只会缓存要加载的第一个。实现动态、级联、模板的三种方式。

  1. 覆盖 Jinja 内置函数。我发现这很令人困惑。我对这个解决方案不够聪明。
  2. 为每个文件提供不同的 WSGI 进程。对于动态站点来说,这里的设置似乎有点太多了。Flask 缓存每个 WSGI 进程的文件名,因此您可以对多个 Cherry WSGI 服务器执行某些操作。
  3. 在加载路径中包含主题。创建一个函数,使用 加载它context_processor,并且使用该函数加载模板文件。该函数需要检查非默认主题,检查文件是否存在,然后返回。因此,模板调用将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') %}

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

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

于 2013-04-14T10:17:55.147 回答