如何在bottle.py中禁用Jinja2模板缓存?

10 投票
4 回答
7739 浏览
提问于 2025-04-17 08:52

我在使用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 个回答

3

在jinja2中,有一个叫做Environment的对象,它有一个配置项可以设置缓存的大小。根据文档的说明,

如果缓存大小设置为0,那么模板就会一直被重新编译。

你试过类似这样的做法吗?

from jinja2 import Environment
env = Environment(cache_size=0)
6

Bottle会在内部缓存模板,这个缓存和Jinja2的缓存是分开的。如果你想关闭这个缓存,可以使用 bottle.debug(True) 或者 bottle.run(..., debug=True)。如果你想清空缓存,可以用 bottle.TEMPLATES.clear()

6

我解决这个问题的方法是完全放弃了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后缀,并且需要把上下文变量以字典的形式传入。

撰写回答