我在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 回答
顺便说一句,您可以将多个位置传递给FileSystemLoader,这是加载模板的推荐方法
这是Apache中的预期行为
mod_wsgi
(我假设您正在使用) . 文件系统更改不会触发所有进程的重新加载 . 请参阅Flask文档中的this entry讨论此问题,并提供一个解决方法,即添加:WSGIScriptReloading On
到应用程序的配置部分,然后
touch
到wsgi
文件以触发重新加载子进程 .你确定这是你想要的吗?大多数主题切换技巧依赖于层叠样式表(CSS)的级联部分来控制主题 .
好吧,我不是唯一一个遇到这个问题的人 . 问题是Flask基于文件名缓存,如果你没有包含相对路径,它只会缓存第一个要加载的路径 . 有三种方法可以完成动态,级联的模板 .
Override Jinja builtins . 我发现这很令人困惑 . 我对这个解决方案不够聪明 .
为每个文件提供不同的WSGI进程 . 对于动态网站来说,这里的设置似乎有点太多了 . Flask为每个WSGI进程缓存文件名,因此您可以使用多个Cherry WSGI服务器执行某些操作 .
在加载路径中包含主题 . 创建一个函数,使用该函数加载
context_processor
和 only 加载模板文件 . 该函数需要检查非默认主题,检查文件是否存在,并返回它 . 所以模板调用将是get_theme_file('layout.html')
,它将返回相对路径(如themes/blue/layout.html
) .选项3的一个例子 .
如果在模板中包含主题文件:
不幸的是,这不会缓存,但我可以看到一些方法来优化它 . 也许缓存
get_theme().theme_dir
的os.listdir
,而不是为每个get_theme_file
调用读取文件系统,只需对缓存的listdir
列表进行in
检查 .值得注意的是,这是Flask特有的 . 我无法使用普通的Jinja2和我自己的WSGI服务器重现这种行为 . 有人可能会说Flask对于这个特定项目来说是一个糟糕的选择,但我认为Flask所做的其他事情的节省是值得的 .