Jinja: 可覆盖的自动转义

创建于 2015-10-15  ·  11评论  ·  资料来源: pallets/jinja

在工作中,我们使用 Jinja 将模板呈现为 YAML。 我们想自定义所有{{ variable }}扩展来对 Python 值做一些有意义的事情,比如None (在 YAML 中是null )和空字符串(它被编码为''在 YAML 中)。 看起来 Jinja 自动转义机制将是完美的。 不幸的是,我没有看到任何方法可以将自动转义功能配置为不自动转义为 HTML。 相反,我不得不在jinja2.runtimeMarkupescape进行猴子补丁。 如果有官方认可的方法来执行此操作,例如通过覆盖环境中的某些内容,那就太好了。

所有11条评论

不确定自动转义是否是执行此操作的正确方法。

我宁愿使用过滤器。 tojson似乎是一个不错的选择,因为 yaml 是 json, 的超集。


除此之外:您为什么要使用基于字符串的模板来构建 YAML? 将 Python 字典序列化为 YAML 似乎更清晰。 我可以看到这可能不够的唯一情况是当您想要包含评论时。 但在这种情况下,您可以使用处理非破坏性读/写的 YAML 引擎,因此您只需加载带有注释和空项目的 YAML 模板,然后更新数据并将其重新序列化为 YAML。

使用自动转义的要点是我不想在每个变量扩展上都放置过滤器。

我们的用例是为我们的微服务编写配置文件。 我们有一个包含在每个服务中的“模板”,我们希望用特定于该模板的每个环境配置来填充它。 我同意直接使用 YAML 更干净,但我们找不到一个通用的“引擎”,它知道如何替换它不知道位置的变量。 (有 YAML 别名和引用,但它们并不真正适合于此——它们更多地用于序列化循环结构。)我可以想到使用 YAML 使其工作的方法,但它们是也有点hacky。 这种设置还可以更好地控制我们可以执行的替换类型,尽管在实践中我们并没有真正将其用于任何事情。

您可以使用 jinja 插件将过滤器应用于所有变量。 只要您不必使用 i18n 块,这并不太难。 也许你可以从我前段时间写的一些东西中得到一些想法: https :

这很有帮助,谢谢! 我觉得自动转义仍然是解决这个问题的好方法,因为您可以将变量标记为|safe 。 但我承认从概念上讲,这并不像自动转义,因为我们不仅要确保变量是有效的 YAML,还要以某种方式格式化它们。 我会写一个扩展。

@glasserc你用这个扩展成功了吗? 我有类似的问题,你能指出解决方案/分享一些想法吗?

我的解决方案的第一部分是通过过滤器运行所有变量调用的扩展。 这是我让它工作所需的最低限度。

import jinja2.ext
from jinja2.lexer import Token

class YAMLEverythingExtension(jinja2.ext.Extension):
    """
    Insert a `|yaml` filter at the end of every variable substitution.

    This will ensure that all injected values are converted to YAML.
    """
    def filter_stream(self, stream):
        # This is based on https://github.com/indico/indico/blob/master/indico/web/flask/templating.py.
        for token in stream:
            if token.type == 'variable_end':
                yield Token(token.lineno, 'pipe', '|')
                yield Token(token.lineno, 'name', 'yaml')
            yield token

这可能更聪明,例如,如果它在变量块中明确看到safeyaml过滤器,它可以尝试跳过插入过滤器。 但我从来没有最终需要/有时间这样做。

第二部分是过滤器本身。 仅使用yaml.dump还不够复杂,所以我不得不稍微浏览一下 yaml 内部结构。

import cStringIO
import yaml

def yaml_filter(val):
    """Serialize some value in isolation, not as part of any document.

    We can't just use yaml.dump because that outputs an entire document, including newlines, which isn't helpful for
    inserting into a YAML document."""
    if isinstance(val, jinja2.Undefined):
        val._fail_with_undefined_error()
    stream = cStringIO.StringIO()
    dumper = yaml.dumper.Dumper(stream)
    dumper.open()
    node = dumper.represent_data(val)
    dumper.serialize(node)
    # The serialized node tends to have a \n at the end.  The template might not
    # want a \n inserted here, e.g. if two variables are on the same line, so
    # strip.
    return stream.getvalue().strip()

这将它们联系在一起,包括确保给定名称的过滤器在环境中可用:

from jinja2 import loaders
from jinja2 import Environment, StrictUndefined

def get_environment():
    """Create a standard Jinja environment that has everything in it.
    """
    jinja_env = Environment(extensions=(YAMLEverythingExtension,),
                            # some other options that we use at work
                            loader=loaders.FileSystemLoader(['.', '/']),
                            undefined=StrictUndefined)
    jinja_env.filters["yaml"] = yaml_filter
    return jinja_env

这对于转义非 HTML 模板仍然非常有用。 我正在使用 Jinja2 生成 JSON,并希望使用 JSON 转义而不是 HTML 转义。

使用 Jinja 模板或任何其他基于字符串的模板语言或字符串操作来生成 JSON 是错误的。 我可以理解您为什么要为 YAML 执行此操作,因为它也是人性化的,但是 JSON 应该从例如 Python dict 而不是从 Jinja 模板生成。

(也就是说,我很好奇您为什么要这样做以及您要做什么:p)

我想这是有道理的。 :-) 我会关闭#571。

也许Jinja 生成JSON 不太好,但是我对这种功能很感兴趣,因为我正在使用jinja 生成LaTeX。 我已经通过修改得到多数的方式有block_start_stringblock_end_string在和其他Environment 。 现在,我已经设置了autoescape = False ,但理想情况下我想传递一些东西来做自己的转义(可能像一个正则表达式)。

这也适用于生成纯文本和降价。 迄今为止,Jinja 一直是用于模板化非 html 文档的出色工具。

我正在研究使用 jinja 来生成 SQL 查询,因为如果您需要多个条件查询部分,使用str.format()会变得混乱。

已经有库可以做到这一点,即jinja-vanish允许自定义转义函数和jinjasql
第一个必须在编译时禁用常量评估,这似乎有点糟糕。 另一个
使用过滤器方法为所有变量表达式添加一个特殊过滤器,这可以但也感觉不对。

我没有深入研究它,但实施它似乎并不难。 可以考虑这样做还是超出了图书馆的范围?

此页面是否有帮助?
0 / 5 - 0 等级