常见问题

本页回答关于 Jinja 的一些常见问题。

为什么叫 Jinja ?

选择 Jinja 是因为它是日本庙宇的名称,而庙宇和模板的发音相似。 它不是以乌干达的城市命名。

它有多快?

我们真的很讨厌基准测试,尤其是因为它们反映的并不多。 模板的性能取决于许多因素,你必须在不同情况下对不同的引擎进行基准测试。 测试套件的基准显示,Jinja 的性能与Mako相似,比 Django 的模板引擎或 Genshi 快 10 到 20 倍。 这些基准测试仅测试几个与性能相关的情形如循环。 一般来说,模板引擎的性能并不重要,因为 Web 应用程序中通常的瓶颈是数据库或应用程序代码。

Jinja 与 Django 的兼容性如何?

Jinja 的默认语法在许多方面与 Django 语法一致。 但是,这种相似性并不意味着你可以在 Jinja 中使用未修改的 Django 模板。 例如,filter 参数使用函数调用语法而不是冒号来分隔 filter 名称和参数。 此外,Jinja 中的扩展接口与 Django 扩展接口有根本的不同,这意味着你的自定义标记将不再工作。

一般来说,你将使用更少的自定义扩展,因为 Jinja 模板系统允许你使用 Python 表达式的特定子集,该子集可以替换大多数 Django 扩展。 例如,不用类似以下的代码:

{% load comments %}
{% get_latest_comments 10 as latest_comments %}
{% for comment in latest_comments %}
    ...
{% endfor %}

你很可能会提供一个具有属性的对象,以便从数据库中检索注释:

{% for comment in models.comments.latest(10) %}
    ...
{% endfor %}

或直接提供用于快速测试的模型:

{% for comment in Comment.objects.order_by('-pub_date')[:10] %}
    ...
{% endfor %}

请记住,即使你可以将这类代码放入模板中,但仍不是个好主意。 查询应进入视图代码,而不是模板!

将逻辑放入模板不是一个可怕的想法吗?

毫无疑问,你应该尝试从模板中删除尽可能多的逻辑。 但是没有任何逻辑的模板意味着你必须在代码中做所有枯燥和愚蠢的处理。 Python 附带的模板引擎称为 string.Template 没有循环和 if 条件,它是迄今为止最快的 Python 模板引擎。

因此,模板中需要一些逻辑来让每个人都满意。 Jinja 留给你很多想放入模板中的逻辑。 你可以做什么,什么不能做,有一些限制。

Jinja 既不允许你将任意 Python 代码放入模板,也不允许所有 Python 表达式。 运算符仅限于最常见的运算符,不支持更高级的表达式,如列表解析和生成器表达式。 这样可以更容易维护模板引擎,模板更易于阅读。

为什么自动转义不是默认行为?

有多种原因导致自动转义不是默认模式,也不是推荐的模式。 虽然变量的自动转义意味着你不太可能出现 XSS 问题,但它也会导致模板引擎中出现大量额外的处理,从而导致严重的性能问题。 由于 Python 不提供将字符串标记为不安全的 Jinja 的方法,因此 Jinja 必须通过提供与安全、不安全的字符串进行安全交互的自定义字符串类(Markup字符串)来破解该限制。

但是,使用显式转义时,模板引擎不必对变量执行任何安全检查。 此外,人类知道不要转义可能永远不会包含字符的整数或字符串,而必须转义字符或 HTML 标记。 例如,在统计表的整数表和浮点上迭代列表时,模板设计器可以省略转义,因为他知道整数或浮点不包含任何不安全的参数。

此外,Jinja 是通用模板引擎,不仅用于 HTML/XML 生成。 例如,你可以生成 LaTeX、emails、CSS、JavaScript 或配置文件。

为什么上下文是不可变的?*

编写contextfunction()或类似内容时,你可能已注意到上下文试图阻止你修改它。 如果你已使用内部上下文 API 成功修改上下文,你可能已经注意到上下文中的更改在模板中似乎不可见。 原因是 Jinja 出于性能仅将上下文用作模板变量的主数据源。

如果要修改上下文,请编写一个函数,然后使用 set 将函数的返回赋值给一个变量:

{% set comments = get_latest_comments() %}

我的回溯看起来很奇怪 发生了什么事情?*

如果未编译 debugsupport 模块,并且你正在使用不带 ctypes 的 Python安装(不带 ctypes 的 Python 2.4 、Jython或Google 的 AppEngine),Jinja 将无法提供正确的调试信息,并且回溯可能不完整。 对于 Jython 或 AppEngine,目前尚无很好的解决方法,因为它们没有 ctypes,因此无法使用 debugsupport 扩展。

如果你在 Google AppEngine 开发服务器中工作,则可以将 ctype 模块列入白名单以恢复回溯。 但是,这在生产环境中不起作用:

import os
if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'):
    from google.appengine.tools.devappserver2.python import sandbox
    sandbox._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt']

此代码片段归功于Thomas Johansson

为什么不支持 Python 2.3/2.4/2.5/2.6/3.1/3.2/3.3 ?

Python 2.3 缺少许多在 Jinja 中大量使用的功能。 做出此决定与即将推出的 Python 2.6 和 3.0 版本一样,维护旧 Python 版本的代码变得更加困难。 如果你真的需要Python 2.3支持,你要么必须使用 Jinja 1 或其它仍然支持 2.3 的模板引擎。

当我们通过相同的源代码(不使用2to3)切换为支持 Python 2 和 3 时,就删除了对 2.4/2.5/3.1/3.2 的支持。 需要放弃支持是因为只有 Python 2.6/2.7 和 +3.3 以相互兼容的方式支持字节和 unicode 文本。 如果你真的需要支持旧的Python 2(或3)版本,你可以只使用Jinja 2.6。

放弃支持 Python 2.6/3.3 是因为它在各种上游项目(如 wheel 或 pytest)中放弃支持,这将使得难以继续支持它。 Jinja 2.10 是支持 Python 2.6/3.3 的最后一个版本。

我的宏被某些东西覆盖|

在某些情况下,Jinja 范围界定似乎是任意的:

layout.tmpl:

{% macro foo() %}LAYOUT{% endmacro %}
{% block body %}{% endblock %}

child.tmpl:

{% extends 'layout.tmpl' %}
{% macro foo() %}CHILD{% endmacro %}
{% block body %}{{ foo() }}{% endblock %}

在 Jinja 中这将打印LAYOUT 这是让父模板在子模板之后进行计算的副作用。 这允许子模板将信息传递到父模板。 为避免此问题,将父模板中的宏或变量重命名为不同的前缀。