扩展|
Jinja 支持扩展,可以添加额外的筛选器、测试、全局,甚至扩展解析器。 扩展的主要动机是将经常使用的代码移动到可重用的类中,如添加对国际化的支持。
添加扩展*
扩展在创建时添加到金贾环境。 创建环境后,无法添加其他扩展。 要添加扩展,将扩展类列表或导入路径传递给Environment
构造函数的extensions
。 以下示例创建加载 i18n 扩展的 Jinja 环境:
jinja_env = Environment(extensions=['jinja2.ext.i18n'])
i18n 扩展|
导入名称: jinja2.ext.i18n
i18n扩展可以与gettext或Babel联合使用。 启用后,Jinja 提供一个trans
语句,将块标记为可翻译并调用gettext
启用后,应用程序必须提供gettext
和ngettext
函数,无论是全局还是呈现时。 _()
函数作为别名添加到gettext
函数。
环境方法|
启用扩展后,环境提供以下其他方法:
-
jinja2.environment.
install_gettext_translations
(translations, newstyle=False)¶ 全局安装环境转换。
translations
对象必须实现gettext
和ngettext
或 Python 2 的ugettext
和ungettext
gettext.NullTranslations
gettext.GNUTranslations
和巴贝尔Translations
是支持的。更改日志
在版本 2.5 中更改: 添加了新样式的 gettext 支持。
-
jinja2.environment.
install_null_translations
(newstyle=False)¶ 安装无操作 gettext 函数。 如果要准备应用程序进行国际化,但不想实现整个系统,这非常有用。
更改日志
在版本 2.5 中更改: 添加了新样式的 gettext 支持。
-
jinja2.environment.
install_gettext_callables
(gettext, ngettext, newstyle=False)¶ 将给定
gettext
和ngettext
可调用安装到环境中。 它们应该与gettext.gettext()
和gettext.ngettext()
或 Python 2 的ugettext
和ungettext
)完全一样。如果激活
newstyle
,则可调用将换行,以像新样式可调用一样工作。 有关详细信息,请参阅New Style Gettext。更改日志
版本 2.5 中的新增功能: 添加了新样式的 gettext 支持。
-
jinja2.environment.
uninstall_gettext_translations
()¶ 卸载环境全局安装的转换。
-
jinja2.environment.
extract_translations
(source)¶ 从给定的模板节点或源中提取可本地化的字符串。
对于找到的每个字符串,此函数都会生成
(线、函数、消息)
元组,其中:lineno
找到字符串的行的编号。function
是使用的gettext
函数的名称(如果字符串是从嵌入的 Python 代码中提取的)。message
是字符串本身(Python 2 上的unicode
),或具有多个参数的函数的字符串组。
如果安装了Babel,请参阅Babel Integration以提取字符串。
对于以多种语言提供但为所有用户提供相同语言的 Web 应用程序(例如,为法语社区安装的多语言论坛软件),可以在创建环境时安装翻译。
translations = get_gettext_translations()
env = Environment(extensions=["jinja2.ext.i18n"])
env.install_gettext_translations(translations)
get_gettext_translations
函数将返回当前配置的转换器,例如通过使用gettext.find
。
the template documentation介绍了模板设计器i18n
扩展的用法。
空白修剪|
更改日志
版本 2.10 中的新增功能。
在[% trans %]
块中,修剪换行符和空格非常有用,以便文本块看起来像翻译文件中具有单个空格的简单字符串。
通过启用ext.i18n.trimmed
policy,可以自动修剪换行符和周围的空白。
新样式获取文本|
更改日志
版本 2.5 中的新增功能。
新样式 gettext 调用键入较少,不易出错,并且支持更好的自动转义。
您可以通过设置env.newstyle_gettext = true
或传递newstyle=True
到env.install_translations
来使用"新样式"gettext 调用。 它们完全受 Babel 提取工具的支持,但可能无法按预期使用其他提取工具。
对于标准gettext
调用,字符串格式设置是使用|format
筛选器完成的单独步骤。 这要求对ngettext
调用进行复制工作。
{{ gettext("Hello, World!") }}
{{ gettext("Hello, %(name)s!")|format(name=name) }}
{{ ngettext(
"%(num)d apple", "%(num)d apples", apples|count
)|format(num=apples|count) }}
新样式gettext
使格式设置成为调用的一部分,并且在后台强制实现更多的一致性。
{{ gettext("Hello, World!") }}
{{ gettext("Hello, %(name)s!", name=name) }}
{{ ngettext("%(num)d apple", "%(num)d apples", apples|count) }}
新样式 gettext 的优点包括:
没有单独的格式设置步骤,您不必记住使用
|format
筛选器。仅允许命名占位符。 这解决了翻译人员面临的常见问题,因为位置占位符无法有意义地切换位置。 命名占位符始终携带有关值流向何处的语义信息。
即使不使用占位符,也会使用字符串格式,这使得所有字符串都使用一致的格式。 记得要逃避任何原始百分比符号
%%
如100%%
翻译的字符串标记为安全,根据需要执行转义格式。 如果参数已转义,则将其标记为
|safe
"。
循环控制|
导入名称: jinja2.ext.loopcontrols
此扩展添加了对break
的支持,并在循环中continue
。 启用后,Jinja 提供了与 Python 完全相同的两个关键字。
写入扩展|
通过编写扩展,您可以将自定义标记添加到 Jinja。 这是一项非同寻常的任务,通常不需要,因为默认标记和表达式涵盖所有常见用例。 i18n 扩展是扩展有用的良好示例。 另一个是片段缓存。
编写扩展时,必须记住您正在使用 Jinja 模板编译器,该编译器不会验证传递给它的节点树。 如果 AST 格式不正确,您将获得各种编译器或运行时错误,这些错误是可怕的调试。 始终确保您正确使用创建的节点。 下面的 API 文档显示了存在哪些节点以及如何使用它们。
扩展示例|
缓存|
下面的示例通过使用缓存库实现 Jinja 的cache
标记:
from jinja2 import nodes
from jinja2.ext import Extension
class FragmentCacheExtension(Extension):
# a set of names that trigger the extension.
tags = {"cache"}
def __init__(self, environment):
super(FragmentCacheExtension, self).__init__(environment)
# add the defaults to the environment
environment.extend(fragment_cache_prefix="", fragment_cache=None)
def parse(self, parser):
# the first token is the token that started the tag. In our case
# we only listen to ``'cache'`` so this will be a name token with
# `cache` as value. We get the line number so that we can give
# that line number to the nodes we create by hand.
lineno = next(parser.stream).lineno
# now we parse a single expression that is used as cache key.
args = [parser.parse_expression()]
# if there is a comma, the user provided a timeout. If not use
# None as second parameter.
if parser.stream.skip_if("comma"):
args.append(parser.parse_expression())
else:
args.append(nodes.Const(None))
# now we parse the body of the cache block up to `endcache` and
# drop the needle (which would always be `endcache` in that case)
body = parser.parse_statements(["name:endcache"], drop_needle=True)
# now return a `CallBlock` node that calls our _cache_support
# helper method on this extension.
return nodes.CallBlock(
self.call_method("_cache_support", args), [], [], body
).set_lineno(lineno)
def _cache_support(self, name, timeout, caller):
"""Helper callback."""
key = self.environment.fragment_cache_prefix + name
# try to load the block from the cache
# if there is no fragment in the cache, render it and store
# it in the cache.
rv = self.environment.fragment_cache.get(key)
if rv is not None:
return rv
rv = caller()
self.environment.fragment_cache.add(key, rv, timeout)
return rv
以下是您在环境中使用它的方式:
from jinja2 import Environment
from cachelib import SimpleCache
env = Environment(extensions=[FragmentCacheExtension])
env.fragment_cache = SimpleCache()
在模板中,可以将块标记为可缓存。 以下示例缓存侧边栏 300 秒:
{% cache 'sidebar', 300 %}
<div class="sidebar">
...
</div>
{% endcache %}
内联gettext
|
下面的示例演示使用Extension.filter_stream()
来分析对_()
gettext 函数的调用,该函数与静态数据并联,而无需 Jinja 块。
<h1>_(Welcome)</h1>
<p>_(This is a paragraph)</p>
它要求加载和配置 i18n 扩展。
# -*- coding: utf-8 -*-
import re
from jinja2.exceptions import TemplateSyntaxError
from jinja2.ext import Extension
from jinja2.lexer import count_newlines
from jinja2.lexer import Token
_outside_re = re.compile(r"\\?(gettext|_)\(")
_inside_re = re.compile(r"\\?[()]")
class InlineGettext(Extension):
"""This extension implements support for inline gettext blocks::
<h1>_(Welcome)</h1>
<p>_(This is a paragraph)</p>
Requires the i18n extension to be loaded and configured.
"""
def filter_stream(self, stream):
paren_stack = 0
for token in stream:
if token.type != "data":
yield token
continue
pos = 0
lineno = token.lineno
while 1:
if not paren_stack:
match = _outside_re.search(token.value, pos)
else:
match = _inside_re.search(token.value, pos)
if match is None:
break
new_pos = match.start()
if new_pos > pos:
preval = token.value[pos:new_pos]
yield Token(lineno, "data", preval)
lineno += count_newlines(preval)
gtok = match.group()
if gtok[0] == "\\":
yield Token(lineno, "data", gtok[1:])
elif not paren_stack:
yield Token(lineno, "block_begin", None)
yield Token(lineno, "name", "trans")
yield Token(lineno, "block_end", None)
paren_stack = 1
else:
if gtok == "(" or paren_stack > 1:
yield Token(lineno, "data", gtok)
paren_stack += gtok == ")" and -1 or 1
if not paren_stack:
yield Token(lineno, "block_begin", None)
yield Token(lineno, "name", "endtrans")
yield Token(lineno, "block_end", None)
pos = match.end()
if pos < len(token.value):
yield Token(lineno, "data", token.value[pos:])
if paren_stack:
raise TemplateSyntaxError(
"unclosed gettext expression",
token.lineno,
stream.name,
stream.filename,
)
扩展 API|
扩展|
扩展始终必须扩展jinja2.ext.Extension
类:
-
class
jinja2.ext.
Extension
(environment)¶ 扩展可用于在解析器级别向 Jinja 模板系统添加额外的功能。 自定义扩展绑定到环境,但可能不会在self上存储特定于环境的数据。 这样做的原因是,通过创建副本并重新分配环境属性,可以将扩展绑定到另一个环境(用于叠加)。
由于扩展是由环境创建的,因此它们不能接受任何配置参数。 人们可能希望通过使用工厂函数来解决这一问题,但这是不可能的,因为扩展由其导入名称标识。 配置扩展的正确方法是在环境中存储配置值。 因为这样,环境最终充当中央配置存储,属性可能会发生冲突,这就是为什么扩展必须确保他们为配置选择的名称不太通用。 例如
prefix
是一个可怕的名称,fragment_cache_prefix
是一个好名称,包括扩展的名称(片段缓存)。-
*标识符
扩展的标识符。 这始终是扩展类的真正导入名称,不得更改。
如果扩展实现自定义标记,这是扩展正在侦听的一组标记名称。
-
attr
(name, lineno=None)¶ 返回当前扩展的属性节点。 这对于在扩展上将常量传递给生成的模板代码很有用。
self.attr('_my_attribute', lineno=lineno)
-
call_method
(name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None)¶ 调用扩展的方法。 这是
attr()
=jinja2.nodes.Call
-
filter_stream
(stream)¶ 它传递了一个
TokenStream
,可用于筛选返回的令牌。 此方法必须返回可迭代的Token
s,但它不必返回TokenStream
。
-
preprocess
(source, name, filename=None)¶ 此方法在实际列词之前调用,并可用于预处理源。 文件名是可选的。 返回值必须是预处理源。
-
解析器|
传递给Extension.parse()
的解析器提供了分析不同类型表达式的方法。 扩展可以使用以下方法:
-
class
jinja2.parser.
Parser
(environment, source, name=None, filename=None, state=None)¶ 这是金雅使用的中央解析类。 它传递给扩展,可用于分析表达式或语句。
-
name
¶ 模板的加载名称。
-
fail
(msg, lineno=None, exc=<class 'jinja2.exceptions.TemplateSyntaxError'>)¶ 使用消息引发exc的方便方法、传递的行号或最后一行号以及当前名称和文件名。
-
free_identifier
(lineno=None)¶ 返回新的免费标识符作为
InternalName
。
-
parse_assign_target
(with_tuple=True, name_only=False, extra_end_rules=None, with_namespace=False)¶ 分析分配目标。 由于 Jinja 允许分纳分配,因此此函数可以解析所有允许的赋值目标。 将分析每个对元组的默认分配,但可以通过将with_tuple设置为False来禁用。 如果仅需要分配给名称name_only则可以设置为True。 extra_end_rules参数转发到元组解析函数。 如果启用with_namespace,则可能会分析命名空间分配。
-
parse_expression
(with_condexpr=True)¶ 分析表达式。 如果未分析可选with_condexpr参数设置为False条件表达式,则分析所有表达式。
-
parse_statements
(end_tokens, drop_needle=False)¶ 将多个语句解析到列表中,直到达到其中一个结束令牌。 这用于分析语句的正文,因为它还会在适当时分析模板数据。 解析器首先检查当前令牌是否为冒号,如果存在冒号,则跳过该分号。 然后,它会检查块端并分析,直到达到end_tokens之一。 根据默认值,调用末尾的流中的活动令牌是匹配的结束令牌。 如果不需要此drop_needle可以设置为True并删除结束令牌。
-
parse_tuple
(simplified=False, with_condexpr=True, extra_end_rules=None, explicit_parentheses=False)¶ 工作原理与parse_expression样,但如果多个表达式被逗号分隔,则创建
Tuple
节点。 如果没有找到逗号,此方法还可以返回正则表达式而不是元组。默认分析模式是完整元组。 如果简化为 True,则仅分析名称和文本。 no_condexpr参数将转发到
parse_expression()
因为元组不需要分隔符,并且可能以假逗号结尾,因此需要额外的提示来标记元组的结尾。 例如,对于循环,支持和中的元数。 在这种情况下,extra_end_rules设置为
['name:in']
如果分析由括号中的表达式触发,则explicit_parentheses为 true。 这用于确定空元组是否有效表达式。
-
-
class
jinja2.lexer.
TokenStream
(generator, name, filename)¶ 令牌流是可迭代的,可生成
Token
s。但是,解析器不会迭代它,而是调用下一个next()
来前面的一个令牌。 当前活动令牌存储为current
。-
property
eos
¶ 我们是在流的末尾吗?
-
expect
(expr)¶ 预期给定的令牌类型并返回它。 这接受与
jinja2.lexer.Token.test()
相同的参数。
-
look
()¶ 查看下一个令牌。
-
next_if
(expr)¶ 执行令牌测试,如果令牌匹配,则返回令牌。 否则,返回值为"无"。
-
push
(token)¶ 将令牌推送回流。
-
skip
(n=1)¶ 前面有 n 个标记。
-
property
-
class
jinja2.lexer.
Token
¶ Token 类。
-
lineno
¶ 标记的行号
-
*
令牌的类型。 此字符串被暂存,因此您可以使用
is
运算符将其与任意字符串进行比较。
-
value
¶ 令牌的值。
-
test
(expr)¶ 针对令牌表达式测试令牌。 这可以是令牌类型,也可以
'token_type:token_value'
这只能针对字符串值和类型进行测试。
-
test_any
(*iterable)¶ 针对多个令牌表达式进行测试。
-
lexer 模块中还有一个实用程序函数,可以计算字符串中的换行符:
-
jinja2.lexer.
count_newlines
(value)¶ 计算字符串中换行符的数量。 这对于筛选流的扩展非常有用。
AST¶
AST(抽象语法树)用于在分析后表示模板。 它是编译器然后转换为可执行 Python 代码对象的节点的生成。 提供自定义语句的扩展可以返回节点以执行自定义 Python 代码。
下面的列表描述了当前可用的所有节点。 AST 可能会在 Jinja 版本之间更改,但将保持向后兼容。
有关详细信息,请查看jinja2.Environment.parse()
.
-
class
jinja2.nodes.
Node
¶ 所有 Jinja 节点的基类。 有许多不同类型的节点可用。 有四种主要类型:
所有节点都有字段和属性。 字段可以是其他节点、列表或任意值。 字段作为常规位置参数传递给构造函数,属性作为关键字参数。 每个节点有两个属性:lineno(节点的行号)和环境。 环境属性自动设置在所有节点的分析过程末尾。
-
find
(node_type)¶ 查找给定类型的第一个节点。 如果不存在此类节点,则返回值为"无"。
-
find_all
(node_type)¶ 查找给定类型的所有节点。 如果类型是元组,则对任何元组项执行检查。
-
iter_child_nodes
(exclude=None, only=None)¶ 遍拉节点的所有直接子节点。 这将遍遍所有字段,并生成它们为节点的值。 如果字段的值是列表,则返回该列表中的所有节点。
-
iter_fields
(exclude=None, only=None)¶ 此方法遍尼遍直列定义的所有字段,并生成
(键、值)
元数。 根据默认值,将返回所有字段,但可以通过提供唯一参数或使用exclude参数排除某些字段来限制某些字段。 两者都应该是字段名称的集或元组。
-
set_ctx
(ctx)¶ 重置节点和所有子节点的上下文。 根据默认值,解析器将生成具有"加载"上下文的节点,因为它是最常见的上下文。 此方法在解析器中用于将分配目标和其他节点设置为存储上下文。
-
set_environment
(environment)¶ 为所有节点设置环境。
-
set_lineno
(lineno, override=False)¶ 设置节点和子节点的行号。
-
-
class
jinja2.nodes.
Expr
| 所有表达式的基类。
- 节点类型
-
as_const
(eval_ctx=None)¶ 如果不可能,则返回表达式的值为常量或引发
Impossible
。如果未为未创建默认上下文,则可以提供
EvalContext
该上下文要求节点具有附加环境。更改日志
在版本 2.4 中更改: 添加了eval_ctx参数。
-
can_assign
()¶ 检查是否可以为此节点分配某些内容。
-
class
jinja2.nodes.
Call
(node, args, kwargs, dyn_args, dyn_kwargs)¶ 调用表达式。 args是参数列表,kwargs关键字参数列表(
Keyword
节点列表),dyn_args和dyn_kwargs必须是"无"或用作动态位置*args
或关键字**kwargs
参数的节点的节点。- 节点类型
-
class
jinja2.nodes.
CondExpr
(test, expr1, expr2)¶ 条件表达式(内联(如果表达式)。 (
[ foo 如果 bar else baz ]
- 节点类型
-
class
jinja2.nodes.
ContextReference
¶ 返回当前模板上下文。 它可以像
Name
节点一样使用,具有'load'
ctx,并将返回当前Context
对象。这里为一个示例,将当前模板名称分配给名为foo的变量:
Assign(Name('foo', ctx='store'), Getattr(ContextReference(), 'name'))
这基本上等同于使用高级 API 时使用
contextfunction()
修饰器,这将导致对上下文的引用作为函数的第一个参数传递。- 节点类型
-
class
jinja2.nodes.
DerivedContextReference
¶ 返回当前模板上下文(包括局部变量)。 与
ContextReference
完全一样,但包括局部变量,如来自for
循环。版本 2.11 中的新增功能。
- 节点类型
-
class
jinja2.nodes.
ExtensionAttribute
(identifier, name)¶ 返回绑定到环境的扩展的属性。 标识符是
Extension
的标识符。此节点通常是通过在扩展上调用
attr()
方法构造的。- 节点类型
-
class
jinja2.nodes.
Filter
(node, name, args, kwargs, dyn_args, dyn_kwargs)¶ 此节点对表达式应用筛选器。 名称是筛选器的名称,其余字段与
Call
相同。如果筛选器的节点为"无",则筛选最后一个缓冲区的内容。 缓冲区由宏和筛选器块创建。
- 节点类型
-
class
jinja2.nodes.
ImportedName
(importname)¶ 如果使用导入名称创建导入名称,则导入名称在节点访问时返回。 例如
ImportedName('cgi.escape')
在计算时从 cgi 模块返回转义函数。 导入由编译器优化,因此无需将它们分配给本地变量。- 节点类型
-
class
jinja2.nodes.
InternalName
(name)¶ 编译器中的内部名称。 您无法自行创建这些节点,但解析器提供了
free_identifier()
方法,该方法会为您创建新的标识符。 此标识符在模板中不可用,并且不受到编译器的特别威胁。- 节点类型
-
class
jinja2.nodes.
Const
(value)¶ 所有常量值。 解析器将返回此节点的简单常量,如
42
或"foo"
但它也可用于存储更复杂的值,如列表。 只有具有安全表示的常量(eval(repr(x)= x
为 true 的对象)。- 节点类型
-
class
jinja2.nodes.
MarkSafeIfAutoescape
(expr)¶ 将包装的表达式标记为安全(将其包装为标记),但前提是自动转义处于活动状态。
更改日志
版本 2.5 中的新增功能。
- 节点类型
-
class
jinja2.nodes.
Name
(name, ctx)¶ 查找名称或将值存储在名称中。 节点的ctx可以是以下值之一:
存储:在名称中存储值
加载:加载该名称
param: 喜欢存储,但如果名称定义为函数参数。
- 节点类型
-
class
jinja2.nodes.
Test
(node, name, args, kwargs, dyn_args, dyn_kwargs)¶ 在表达式上应用测试。 名称是测试的名称,其余字段与
Call
相同。- 节点类型
-
class
jinja2.nodes.
Operand
(op, expr)¶ 保存运算符和表达式。 以下运算符可用:
%
**
,+
-
, , ,//
/
notin
ne
lteq
lt
not
*
eq
gt
gteq
in
- 节点类型
-
class
jinja2.nodes.
CallBlock
(call, args, defaults, body)¶ 就像没有名称但调用的宏。 调用调用与未命名的宏作为调用方参数此节点持有。
- 节点类型
-
class
jinja2.nodes.
EvalContextModifier
(options)¶ 修改评估上下文。 对于应修改的每个选项,必须将
Keyword
添加到options
列表中。更改自动转义设置的示例:
EvalContextModifier(options=[Keyword('autoescape', Const(True))])
- 节点类型
-
class
jinja2.nodes.
ScopedEvalContextModifier
(options, body)¶ 修改评估上下文,并在以后还原它。 工作完全像
EvalContextModifier
,但只会修改body
中节点的EvalContext
- 节点类型
-
class
jinja2.nodes.
For
(target, iter, body, else_, test, recursive)¶ for 循环。 目标是迭代的目标(通常是
Name
或Tuple
),迭代可迭代。 body是用作循环体的节点的列表,else_ else块的节点列表。 如果不存在其他节点,则该节点必须为空列表。对于筛选的节点,表达式可以存储为测试,否则为 None。
- 节点类型
-
class
jinja2.nodes.
FromImport
(template, names, with_context)¶ 表示来自导入标记的节点。 请务必不要将不安全的名称传递给名称属性。 编译器将属性查找直接转换为 getattr 调用,并且不使用接口的子脚本回调。 由于导出的变量可能不会以双下划线(解析器断言)开头,因此对于常规的 Jinja 代码来说,这不是问题,但如果此节点在扩展中使用,则必须格外小心。
如果需要别名,名称列表可能包含元数。
- 节点类型
-
class
jinja2.nodes.
Macro
(name, args, defaults, body)¶ 宏定义。 名称是宏的名称,是参数列表,默认值列表(如果有)。 正文是宏体的节点列表。
- 节点类型
-
class
jinja2.nodes.
OverlayScope
(context, body)¶ 扩展的叠加范围。 这是一个基本上未优化的范围,但可用于将完全任意的变量引入字典或字典(如对象)的子作用域。 上下文字段必须对字典对象进行评估。
示例用法:
OverlayScope(context=self.call_method('get_context'), body=[...])
更改日志
版本 2.10 中的新增功能。
- 节点类型
-
class
jinja2.nodes.
With
(targets, values, body)¶ 具有 语句的特定节点。 在旧版本的 Jinja 中,在Scope节点的基础上实现了 with 语句。
更改日志
版本 2.9.3 中的新增功能。
- 节点类型
-
exception
jinja2.nodes.
Impossible
¶ 如果节点无法执行请求的操作,则引发。