如何在bottle.py中禁用Jinja2模板缓存?
我在使用Jinja2模板和Bottle.py,还有Google App Engine的dev_appserver进行开发。我希望模板在每次请求时都能自动重新加载(或者理想情况下,只在它们发生变化时重新加载),这样我就不需要一直重启服务器。
根据Bottle的文档,你应该可以通过调用 bottle.debug(True)
来关闭模板缓存。
不过,Jinja似乎还是在缓存它的模板。我觉得这可能是因为Bottle的Jinja2适配器的写法(它只是使用了默认的Jinja2加载器,并没有提供很多配置选项)。
根据Jinja2文档,我写了一个自定义的加载器,我本以为它能让模板每次都重新加载,但似乎也没有效果:
import settings
from bottle import jinja2_template
from bottle import Jinja2Template, template as base_template
class AutoreloadJinja2Template(Jinja2Template):
def loader(self, name):
def uptodate():
# Always reload the template if we're in DEVMODE (a boolean flag)
return not settings.DEVMODE
fname = self.search(name, self.lookup)
if fname:
with open(fname, "rb") as f:
source = f.read().decode(self.encoding)
return (source, fname, uptodate)
template = functools.partial(base_template,
template_adapter=AutoreloadJinja2Template,
template_lookup = settings.TEMPLATE_PATHS,
template_settings={
'auto_reload': settings.DEVMODE
}
)
模板仍然在缓存中,直到我重启dev_appserver。这应该是一个相当常见的问题。有没有人有有效的解决方案?
更新:
我最后做了类似这样的事情:
class CustomJinja2Template(Jinja2Template):
if settings.DEVMODE:
def prepare(self, *args, **kwargs):
kwargs.update({'cache_size':0})
return Jinja2Template.prepare(self, *args, **kwargs)
template = functools.partial(original_template, template_adapter=CustomJinja2Template)
这会导致模板总是重新加载,但只有当某个Python模块被修改时。也就是说,如果你只是编辑一个模板文件,变化不会生效,直到你编辑一个导入了它的Python文件。看起来模板还是在某个地方被缓存了。
4 个回答
在jinja2中,有一个叫做Environment的对象,它有一个配置项可以设置缓存的大小。根据文档的说明,
如果缓存大小设置为0,那么模板就会一直被重新编译。
你试过类似这样的做法吗?
from jinja2 import Environment
env = Environment(cache_size=0)
Bottle会在内部缓存模板,这个缓存和Jinja2的缓存是分开的。如果你想关闭这个缓存,可以使用 bottle.debug(True)
或者 bottle.run(..., debug=True)
。如果你想清空缓存,可以用 bottle.TEMPLATES.clear()
。
我解决这个问题的方法是完全放弃了bottle的模板方案,改用纯粹的jinja2。看起来,Jinja的FileSystemLoader
是唯一一个可以监控文件变化的工具。
我定义了一个新的template
函数,具体如下(它会在views/
文件夹中查找文件,就像bottle之前那样):
from jinja2 import Environment, FileSystemLoader
if local_settings.DEBUG:
jinja2_env = Environment(loader=FileSystemLoader('views/'), cache_size=0)
else:
jinja2_env = Environment(loader=FileSystemLoader('views/'))
def template(name, ctx):
t = jinja2_env.get_template(name)
return t.render(**ctx)
然后我这样使用它:
@route('/hello')
def hello():
return template('index.tpl', {'text': "hello"})
和bottle的API不同的是,你需要在文件名中加上.tpl
后缀,并且需要把上下文变量以字典的形式传入。