模板

Flask 使用 Jinja2 作为默认的模板渲染引擎。你可以自由更换其他的模板渲染引擎,但是你必须安装 Jinja2 来运行 Flask 本身。这个要求对于实现丰富的扩展是必要的。因为有些扩展使用 Jinja2 来实现功能。

本节只简要介绍了 Jinja2 是如何集成到Flask中的。如果你想知道有关 Jinja2 的更多语法,请访问官方的 Jinja2 模板文档 来获取更多信息。

Jinja 设置

除非被自定义,Jinja2 被 Flask 配置为以下内容:

  • 当使用 render_template() 时,所有文件名以 .html.htm.xml.xhtml 以及 .svg 结尾的模板被配置了自动转义。

  • 当使用 render_template_string() 时,所有字符串会被自动转义。

  • 模板可以使用 {% autoescape %} 以选择是否使用自动转义。

  • 除了默认值之外,Flask 还在 Jinja2 上下文中插入了一些全局函数和辅助函数。

标准上下文

默认情况下,以下全局变量在 Jinja2 模板中可用:

config

当前配置对象(flask.Flask.config

Changelog

在 0.10 版本发生变更: 即使是在导入的模板中,这些也始终可用。

Added in version 0.6.

request

当前请求对象(flask.request)。如果模板在没有激活请求上下文的情况下被渲染,则此变量不可用。

session

当前会话对象(flask.session)。如果模板在没有激活请求上下文的情况下被渲染,则此变量不可用。

g

请求绑定全局变量(flask.g)。如果模板在没有激活请求上下文的情况下被渲染,则此变量不可用。

url_for()

flask.url_for() 函数。

get_flashed_messages()

flask.get_flashed_messages() 函数。

Jinja 上下文控制

这些变量被添加到变量的上下文中,它们不是全局变量。不同之处在于,默认情况下,这些不会出现在导入模板的上下文中。这样做一部分是由于性能方面的考虑,另一部分是为了保持显式。

这对你来说意味着什么?如果你有一个要导入的宏,需要访问请求对象,则有两种可能性:

  1. 显式地将请求作为参数或感兴趣的请求对象的属性传递给宏。

  2. 导入宏并“携带上下文”.

像下面这样导入上下文:

{% from '_helpers.html' import my_macro with context %}

自动转义控制

自动转义是指为你自动转义特殊字符。HTML(或 XML,以及 XHTML)意义上的特殊字符是 &><" 以及 '。因为这些字符在文档中本身就有特定的含义,如果你想在文本中使用它们,你就必须用所谓的“实体”来代替它们。不这样做不仅会让用户因为无法在文本中用这些字符而感到沮丧,还会导致安全问题。(请参阅 Cross-Site Scripting (XSS)

但是,有时你需要在模板中禁用自动转义。如果你想将 HTML 显式地注入页面,例如,如果它们来自生成安全 HTML 的系统(如 markdown 到 HTML 转换器),则可能会出现这种情况。

有三种方法可以做到这一点:

  • 在 Python 代码中,在将 HTML 字符串传递给模板之前,将其包装在 Markup 对象中。这通常是推荐的方式。

  • 在模板中,使用 |safe 筛选器将字符串显式标记为安全 HTML({{ myvariable|safe }}

  • 暂时完全禁用自动转义系统。

要禁用模板中的自动转义系统,可以使用 {% autoescape %} 块:

{% autoescape false %}
    <p>autoescaping is disabled here
    <p>{{ will_not_be_escaped }}
{% endautoescape %}

每当你这样做的时候,请对你在这个块中使用的变量非常谨慎。

注册筛选器

如果你想在 Jinja2 中注册你自己的过滤器,你有两种方法。你可以手动将它们放入应用程序的 jinja_env 中,也可以使用 template_filter() 装饰器。

以下两个示例的工作原理相同,都反转了一个对象:

@app.template_filter('reverse')
def reverse_filter(s):
    return s[::-1]

def reverse_filter(s):
    return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter

在使用装饰器的情况下,如果你想使用函数名作为过滤器的名称,那么参数是可选的。注册后,你可以在模板中使用与 Jinja2 内置过滤器相同的过滤器,例如,如果你在上下文中有一个名为 mylist 的 Python 列表:

{% for x in mylist | reverse %}
{% endfor %}

上下文处理器

Flask 中的上下文处理器可以将新变量自动注入到模板的上下文中。上下文处理器在渲染模板之前运行,并且能够将新值注入到模板上下文中。上下文处理器是一个返回字典的函数。此字典的键和值将与应用程序中所有模板的模板上下文合并:

@app.context_processor
def inject_user():
    return dict(user=g.user)

上面的上下文处理器使一个名为 user 的变量在模板中可用,值为 g.user。这个例子不是很有趣,因为 g 在模板中无论如何都是可用的,但它给出了一个如何工作的想法。

变量不限于值;上下文处理器还可以使函数可用于模板(因为 Python 允许传递函数):

@app.context_processor
def utility_processor():
    def format_price(amount, currency="€"):
        return f"{amount:.2f}{currency}"
    return dict(format_price=format_price)

上面的上下文处理器使 format_price 函数可用于所有模板:

{{ format_price(0.33) }}

你也可以将 format_price 构建为模板过滤器(请参阅 注册筛选器),但这演示了如何在上下文处理器中传递函数。

流式渲染

有时候不需要将整个模板渲染为一个完整的字符串,而是可以将其作为一个数据流来渲染,逐步产出较小的字符串片段。这种方式可以用于分块传输 HTML,从而加快页面的首次加载速度,或者在渲染非常大的模板时节省内存。

Jinja2 模板引擎支持按片段渲染模板,返回一个字符串迭代器。Flask 提供了 stream_template()stream_template_string() 函数,使得这种用法更加方便。

from flask import stream_template

@app.get("/timeline")
def timeline():
    return stream_template("timeline.html")

如果请求处于活动状态,这些函数会自动应用 stream_with_context() 包装器,以确保在模板中仍然可以访问请求上下文。