模板

Flask 利用 Jinja2 作为其模板引擎。您显然可以自由使用不同的模板引擎,但您仍然必须安装 Jinja2 才能运行 Flask 本身。此要求对于启用丰富的扩展是必要的。扩展可以依赖于 Jinja2 的存在。

本节仅简要介绍 Jinja2 如何集成到 Flask 中。如果您想了解模板引擎语法的相关信息,请访问官方 Jinja2 模板文档 获取更多信息。

Jinja 设置

除非自定义,否则 Jinja2 由 Flask 配置如下

  • 当使用 render_template() 时,对于所有以 .html, .htm, .xml, .xhtml 以及 .svg 结尾的模板,自动转义功能是启用的。

  • 当使用 render_template_string() 时,对于所有字符串,自动转义功能是启用的。

  • 模板可以使用 {% autoescape %} 标签选择启用/禁用自动转义。

  • 除了默认存在的值之外,Flask 还在 Jinja2 上下文中插入了几个全局函数和助手函数。

标准上下文

以下全局变量在 Jinja2 模板中默认可用

config

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

更新日志

在 0.10 版本中变更: 现在始终可用,即使在导入的模板中也是如此。

在 0.6 版本中添加。

request

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

session

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

g

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

url_for()

flask.url_for() 函数。

get_flashed_messages()

flask.get_flashed_messages() 函数。

Jinja 上下文行为

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

这对您意味着什么?如果您有一个想要导入的宏,并且需要访问 request 对象,您有两种可能性

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

  2. 您“带上下文”导入宏。

带上下文导入看起来像这样

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

控制自动转义

自动转义是为您自动转义特殊字符的概念。HTML(或 XML,以及 XHTML)意义上的特殊字符是 &, >, <, " 以及 '。因为这些字符在文档中本身就带有特定的含义,所以如果您想将它们用于文本,则必须将它们替换为所谓的“实体”。不这样做不仅会导致用户因无法在文本中使用这些字符而感到沮丧,而且还可能导致安全问题。(请参阅 跨站脚本攻击 (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() 包装器,以便它在模板中仍然可用。