在瓶中使用 jinja2 中的自定义过滤器

Custom filter in jinja2 using in bottle

我尝试使用 bottle (not flask). Suppose I want to define a pandoc custom filter. Regarding the jinja2 documentation 在 jinja2 中定义自定义过滤器,我必须这样定义它:

from bottle import jinja2_view as view, jinja2_template as template
from jinja2 import environment, filters

def pandoc_convert(s):
    return pypandoc.convert_text(s, format='md', to='html5')

@get("/foo")
def foo():
    return template('foo.html')

environment = jinja2.Environment()
# Putting my custom filter.
environment.filters['pandoc'] = pandoc_convert
run(host='localhost', port=8080, debug=True)

在模板中 foo.html 我有

  {{ "This is *some* markdown" | pandoc }}

当我 运行 瓶子代码时,它让我:

jinja2.exceptions.TemplateAssertionError: No filter named 'pandoc'

但如果您打印 environment.filters,则 'pandoc': <function pandoc_convert at 0x7f827e233040>} 会出现在该打印中。

(已更新)

此答案提供了使用 Jinja 的一般说明,但是 看起来好像 bottle 有自己特殊的做事方式。你 需要忽略关于环境和过滤器的 Jinja 文档, 而是深入研究瓶子的来源;具体来说, Jinja2Template,看起来像这样:

class Jinja2Template(BaseTemplate):
    def prepare(self, filters=None, tests=None, globals={}, **kwargs):
        from jinja2 import Environment, FunctionLoader
        self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
        if filters: self.env.filters.update(filters)
        ...

请注意,prepare 方法接受一个 filters 关键字参数,用于在其创建的环境中设置过滤器。

jinja2_template函数,定义如下:

jinja2_template = functools.partial(template, template_adapter=Jinja2Template)

最后,template 函数,其中包括:

    if tplid not in TEMPLATES or DEBUG:
        settings = kwargs.pop('template_settings', {})
        if isinstance(tpl, adapter):
            TEMPLATES[tplid] = tpl
            if settings: TEMPLATES[tplid].prepare(**settings)
        elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
            TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)
        else:
            TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings)

所以,当您调用 jinja2_template("foo.html")(这就是您 做),这就变成了:

template("foo.html", template_adapter=Jinja2Template)

并且在 template 函数中,模板将调用 prepare Jinja2Template 的方法,关键字参数来自 settings, 它来自 template_settingstemplate 的参数。所以, 我们可以使用这样的自定义过滤器:

import bottle


def hellofilter(value):
    return value.replace("hello", "goodbye")


@bottle.get("/foo")
def foo():
    settings = {
        "filters": {
            "hello": hellofilter,
        }
    }
    return bottle.jinja2_template("foo.html", template_settings=settings)


if __name__ == "__main__":
    bottle.run(host="127.0.0.1", port=7070, debug=True)

如果我的模板 foo.html 如下所示:

This is a test. {{ "hello world"|hello }}

请求 127.0.0.1:7070/foo 将 return:

This is a test. goodbye world

如果我们不想每次都传递 template_settings 参数 我们调用 jinja2_template,我们可以自己使用 functools.partialjinja2_template 函数创建包装器:

import bottle
import functools


def hellofilter(value):
    return value.replace("hello", "goodbye")


template_settings = {
    "filters": {
        "hello": hellofilter,
    },
}
template = functools.partial(
    bottle.jinja2_template, template_settings=template_settings
)


@bottle.get("/foo")
def foo():
    return template("foo.html")


if __name__ == "__main__":
    bottle.run(host="127.0.0.1", port=7070, debug=True)