python jinjia2 高级


高层api

class jinja2.Environment([options])

Enviroment:环境,是Jinjia2的核心组件,它包含重要的共享变量,如配置、过滤器、测试、全局变量、模板加载器等。上述的使用模板加载器就是使用到了loader参数来加载我们指定的模板资源。

构造方法中的属性

  • block_start_string
    标记块开始的字符串。 默认为{%
  • block_end_string
    标识块结束的字符串。默认为%}
  • variable_start_string
    标记打印语句开头的字符串,默认为{{
  • variable_end_string
    标识打印语句结束的字符串, 默认为}}
  • comment_start_string
    标识注释的开始的字符串, 默认为{#
  • comment_end_string
    表示注释的结束的字符串,默认为#}
  • line_statement_prefix
    如果给定和一个字符串,这将用作基于行的语句的前缀, 相当于是是{% %}的另一种写法,如下当设置了此属性为#时,这两个含义是一样的
    <ul>
        # for item in seq
        <li>{{ item }}</li>
        # endfor
    </ul>

    <ul>
        {% for item in seq %}
        <li>{{ item }}</li>
        {% endfor %}
    </ul>

  • line_comment_prefix
    2.2中新增,如果给定和一个字符串,这将用作基于行的注释的前缀, 相当于是{# #}的另一种写法。当设置此属性为##, 以下两种写法都是可以的:
    <ul>
        # for item in seq 
        <li>{{ item }}</li> ## test
        # endfor
    </ul>

    <ul>
        {% for item in seq %} {# test #}
        <li>{{ item }}</li>
        {% endfor %}
    </ul>

需要注意的是line_statement_prefixline_comment_prefix最好不要在同一行出现,尤其是设置的类似的时候。

  • trim_blocks
    如果将其设置为 True 删除块后的第一个换行符(块,而不是变量标记!)。默认为false;去除block之间的一个换行符,当换行符多的情况下也只会去除一个换行符,转换示例如下:
    <ul>
 

        <li>1</li>
        
 

        <li>2</li>
        
 

        <li>3</li>
        

    </ul>
    <ul>
 
        <li>1</li>
        
 
        <li>2</li>
        
 
        <li>3</li>
        
    </ul>
  • lstrip_blocks
    如果将其设置为 True,则前导空格和制表符将从行首剥离到块中。默认为false, 。
    <ul> 
            <li>1</li>
         
            <li>2</li>
         
            <li>3</li>
         </ul>
    <ul> 
            <li>1</li>
 
            <li>2</li>
 
            <li>3</li>
 </ul>
  • newline_sequence
    开始换行符的序列。 必须是/r/n/r/n之一。 默认值为 /n,这对于 Linux 和 OS X 系统以及 Web 应用程序来说是一个有用的默认值。相当于是每行的行尾是啥,由于不同的系统对于换行符要求有所不同,
Unix系统里,每行结尾只有“<换行>”,即"/n";Windows系统里面,每行结尾是“<回车><换行>”,即“/r/n”;Mac系统里,每行结尾是“<回车>”,即"/r";。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
  • keep_trailing_newline
    渲染模板时保留尾随换行符。 默认值为 False,这会导致从模板末尾删除单个换行符(如果存在),是针对于模板内容的末尾换行符,当设置为True时会删除模板内容的一个末尾换行符,转换如下:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul>

            <li>1</li>






            <li>3</li>


    </ul>
</body>

</html>


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul>

            <li>1</li>



            <li>2</li>



            <li>3</li>


    </ul>
</body>

</html>

如上述所示,少了一个换行符

  • extensions
    要使用的 Jinja 扩展列表。 这可以是作为字符串或扩展类的导入路径
  • optimized
    是否启用优化器, 默认为真。
  • undefined
    未定义或其子类,用于表示模板中未定义的值。当未传值到对应模板中时的默认值
  • finalize
    可用于在输出变量表达式之前处理其结果的可调用对象。 例如,可以在这里将 None 隐式转换为空字符串。可以拿到传递给模板的值然后做处理。
  • autoescape
    如果设置为 true,则默认启用 XML/HTML 自动转义功能。 有关自动转义的更多详细信息,下方会有所体现。 从 Jinja 2.4 开始,这也可以是传递模板名称的可调用对象,并且必须根据默认情况下启用自动转义返回 True 或 False。
  • loader
    此环境的模板加载器。
  • cache_size
    缓存的大小。 默认情况下这是 400,这意味着如果加载了超过 400 个模板,加载器将清除最近最少使用的模板。 如果缓存大小设置为 0,模板会一直重新编译,如果缓存大小为 -1,则不会清理缓存。jinjia2 2.8从50改为了400。
  • auto_reload
    一些加载器从模板源可能更改的位置(即:文件系统或数据库)加载模板。 如果每次请求模板时将 auto_reload 设置为 True(默认),则加载器会检查源是否更改,如果是,它将重新加载模板。 为了获得更高的性能,可以禁用它。这是为了防止模板在使用过程中出现变动,从而重新加载模板,类似于热插拔。
  • bytecode_cache
    如果设置为字节码缓存对象,该对象将为内部 Jinja 字节码提供缓存,这样如果模板没有更改,就不必解析它们。

方法

  • overlay([options])
    创建一个新的覆盖环境,与当前环境共享所有数据,除了缓存和覆盖的属性。 无法删除覆盖环境的扩展。 覆盖的环境会自动获取它所链接的环境的所有扩展以及可选的额外扩展。
    创建覆盖应该在初始环境完全设置之后发生。 并非所有属性都真正关联,有些属性只是被复制过来,因此对原始环境的修改可能无法通过。
  • undefined([hint, obj, name, exc])
  • add_extension(extension)
    为当前环境添加扩展
  • compile_expression(source, undefined_to_none=True)
    2.1中新增
    一个方便的辅助方法,它返回一个可调用对象,该可调用对象接受作为变量出现在表达式中的关键字参数。 如果被调用,则返回表达式的结果。默认情况下,如果表达式返回未定义的值,则返回值将转换为 None。 这可以通过将 undefined_to_none 设置为 False 来更改。
  • compile_templates(target, extensions=None, filter_func=None, zip=’deflated’, log_function=None, ignore_errors=True, py_compile=False)
    找到加载器可以找到的所有模板,编译它们并将它们存储在目标中。 如果 zip 为 None,而不是在 zipfile 中,模板将存储在一个目录中。 默认情况下,使用 deflate zip 算法。 要切换到存储算法,可以将 zip 设置为“已存储”。extensions 和 filter_func 被传递给 list_templates()。 返回的每个模板都将编译到目标文件夹或 zipfile。默认情况下,模板编译错误被忽略。 如果提供了日志功能,则会记录错误。 如果您希望模板语法错误中止编译,您可以将 ignore_errors 设置为 False,您将收到语法错误异常。
  • extend(**attributes)
    如果项目尚不存在,则将它们添加到环境实例中。 扩展使用它来注册回调和配置值,而不会破坏继承。
  • from_string(source, globals=None, template_class=None)
    从字符串中加载模板,返回一个模板对象
  • get_or_select_template(template_name_or_list, parent=None, globals=None)
    2.3中新增
    如果给出了模板名称的可迭代对象,则进行类型检查并分派给 select_template(),否则分派给 get_template()
  • get_template(name, parent=None, globals=None)
    从加载器加载模板。 如果配置了加载器,则此方法向加载器询问模板并返回模板。 如果 parent 参数不是 None,则在加载前调用 join_path() 以获取真实的模板名称。globals 参数可用于提供模板范围的全局变量。 这些变量在渲染时在上下文中可用。如果模板不存在,则会引发 TemplateNotFound 异常。
  • list_templates(extensions=None, filter_func=None)
    2.4新增
    返回当前环境下的所有模板列表,这个需要加载器支持,如果模板文件夹还有其他文件,可以使用filter_func参数做过滤。
  • select_template(names, parent=None, globals=None)
    2.3中新增
    与get_template类似,不过会在失败前尝试多个模板,最后返回模板失败。
    返回一个环境的模板列表,需要加载器支持

classjinja2.Template

核心的模板对象,由Enviroment对象产生,对于一个模板对象来说,是不可修改的。

  • globals
    该模板的全局变量字典。修改这个字典是不安全的,因为它可能与其它模板或 加载这个模板的环境共享。
  • name
    模板的加载名。如果模板从字符串加载,这个值为 None 。
  • filename
    模板在文件系统上的文件名,如果没有从文件系统加载,这个值为 None 。
  • render([context])
    传入dict()或者以关键字参数形式传入,渲染模板文件并返回数据。
  • generate([context])
    render类似,不过是针对于大文件进行渲染, 逐行逐句,返回一个生成器
  • stream([context])
    generate类似。不过返回TemplateStream对象
  • make_module(vars=None, shared=False, locals=None)
    此方法在不带参数调用时类似于模块属性,但它会在每次调用时评估模板而不是缓存它。 也可以提供一个字典,然后将其用作上下文。 参数与 new_context() 方法的参数相同。
  • module
    模板作为模块。 这用于模板运行时中的导入,但如果想要从 Python 层访问导出的模板变量也很有用:

classjinja2.environment.TemplateStream

模板流的工作方式与普通的 Python 生成器非常相似,但它可以缓冲多个项目以减少总迭代次数。 默认情况下,输出是无缓冲的,这意味着对于模板中的每条无缓冲指令,都会产生一个 unicode 字符串。如果在缓冲区大小为 5 的情况下启用缓冲,则将五个项目组合成一个新的 unicode 字符串。 如果您通过 WSGI 将大模板流式传输到客户端,这在每次迭代后刷新,这主要是有用的。

  • disable_buffering()
    禁止缓存
  • dump(fp, encoding=None, errors=’strict’)
    将完整的流转储到文件或类似文件的对象中。 默认情况下会写入 unicode 字符串,如果要在写入之前进行编码,请指定编码
  • enable_buffering(size=5)
    启用缓冲。 在产生它们之前缓冲大小项目。

自动转义

自动转义就是自动帮你将特殊的字符替换成转义符号。HTML(或者XML, XHTML)的特殊字符有 &, >, <, “, ‘。如下

<h1>hello</h1>

当开启自动转义后,将无法正常显示标签的含义。因此,在某些情况下,我们需要关闭自动转义或者对特定的文件格式做自动转义,可以使用Environment中的autoescape自定义转义格式要求,如下只对html、html、xml做自动转义:

def guess_autoescape(template_name):
    if template_name is None or '.' not in template_name:
        return False
    ext = template_name.rsplit('.', 1)[1]
    return ext in ('html', 'htm', 'xml')

env = Environment(autoescape=guess_autoescape,
                  loader=PackageLoader('mypackage'),
                  extensions=['jinja2.ext.autoescape'])

标识符的说明

在Jinjia2中,python3以上,有效的标识符必须匹配 [a-zA-Z_][a-zA-Z0-9_]*, 也就是必须由字母、数字、下划线组成。但是对于过滤器(filters)和测试(tests)来说,过滤器和测试标识符的正则表达式是 [a-zA-Z_][a-zA-Z0-9_]*(/.[a-zA-Z_][a-zA-Z0-9_]*)*,相比有效标识符,多了个.

未定义类型

待更新

上下文

  • classjinja2.runtime.Context
    jinjia2中的上下文,主要用于管理保存模板中的变量,上下文是不可变的并且只支持只读字典读取操作。

加载器

从类似于文件、数据库、其他资源中加载模板,并会保存在内存中,但是默认缓存有大小限制,由Environmentcache_size控制,加载器也可以自己重写,只需要继承BaseLoader并重载 get_source ,基础的示例如下:

from jinja2 import BaseLoader, TemplateNotFound
from os.path import join, exists, getmtime

class MyLoader(BaseLoader):

    def __init__(self, path):
        self.path = path

    def get_source(self, environment, template):
        path = join(self.path, template)
        if not exists(path):
            raise TemplateNotFound(template)
        mtime = getmtime(path)
        with open(path) as f:
            source = f.read()
        return source, path, lambda: mtime == getmtime(path)

方法

  • get_source(environment, template)
    获取模板的模板源、文件名和重新加载帮助器。它传递了环境和模板名称,并且必须返回一个元组,元组形式为 (source, filename, uptodate) ,如果找不到模板,则会出现TemplateNotFound错误。
    source: 模板中的字符串
    filename:模板所在的路径字符串
    uptodate:模板是否出现修改后,进行自动重新加载
  • load(environment, name, globals=None)
    加载一个模板,从缓存中查找,加载器(例如:class:PrefixLoader 或:class:ChoiceLoader),不会调用此方法,而是直接调用 get_source

内置的加载器

1.classjinja2.FileSystemLoader(searchpath, encoding=’utf-8′, followlinks=False)
从文件系统加载模板路径,是文件系统加载的首选方式。推荐使用

  • searchpath: 查找的路径,可以是路径字符串、路径对象、路径列表
  • encoding: 加载模板的编码,默认为utf-8
  • followlinks: 是否按照路径中的符号链接。默认为false

2.classjinja2.PackageLoader(package_name, package_path=’templates’, encoding=’utf-8′)
从python的包中加载模板,此方法对于路径要求较为严苛,类似于代码中的导入

  • package_name:加载的包名
  • package_path:包名中的模板文件夹,默认为templates
  • encoding: 加载模板的编码,默认为utf-8

3.classjinja2.DictLoader(mapping)
从python中的字典导入,将所有的模板文件以{模板文件名称:模板文件路径}的形式存放于字典中

  • mapping:需要加载的字典,键值对可为多个

4.classjinja2.FunctionLoader(load_func)
从方法中加载模板,一般在方法中做判断,返回模板文件

  • load_func:需要加载的方法
def load_template(name):
    if name == 'index.html':
        return '...'
loader = FunctionLoader(load_template)

5.classjinja2.PrefixLoader(mapping, delimiter=’/’)

  • mapping: 传入字典,以{前缀名:加载器}

将不同的模板文件夹分类,添加前缀,方便后续调用模板操作。例如

loader = PrefixLoader({
    'app1':     PackageLoader('mypackage.app1'),
    'app2':     PackageLoader('mypackage.app2')
})

此时使用app1/template.html就可以获取到mypackage.app1下面的模板文件了
6.classjinja2.ChoiceLoader(loaders)
依次查找模板,直到找到存在的模板停止

loader = ChoiceLoader([
    FileSystemLoader('/path/to/user/templates'),
    FileSystemLoader('/path/to/system/templates')
])

7.classjinja2.ModuleLoader(path)
从预编译的模板里加载模板,模板可以使用compile_templates进行预编译,然后指定路径即可

  • path:预编译模板的路径

字节码缓存

有利于加载模板时使用

实用工具

  • jinja2.environmentfilter(f)
  • jinja2.contextfilter(f)
  • jinja2.evalcontextfilter(f)
    装饰器,被修饰的方法或函数的第一个参数可以获取过滤器的当前环境、上下文、求值上下文,方便后续操作,例如查看是否开启自动转义、当前的模板文件及模板文件列表等。
  • jinja2.environmentfunction(f)
  • jinja2.contextfunction(f)
  • jinja2.evalcontextfunction(f)
    装饰器,被修饰的函数或方法的第一个参数可以获取当前的环境、上下文、求值上下文,例如查看是否开启自动转义、当前的模板文件及模板文件列表等。
    上述六个方法都是类似的,都是为了获取环境、上下文等,在jinjia3.0之后被pass_environment, pass_eval_context, pass_context替代,下面的自定义过滤器中有描述。
  • jinja2.escape(s)
    jinjia2 3.1.0之后,需from markupsafe import escape导入
    把字符串 s 中 & 、 < 、 > 、 ‘ 和 ” 转换为HTML安全的序列。如果你需要在 HTML 中显示可能包含这些字符的文本,可以使用它,就是相当于转义。
import pathlib
from jinja2 import FileSystemLoader, pass_environment
from markupsafe import escape

template_path = (
    pathlib.Path(__file__).parent.joinpath("template_demo").joinpath("templates")
)

env = Environment(
    trim_blocks=True, lstrip_blocks=True, loader=FileSystemLoader(template_path)
)


@pass_environment
def fun(env: Environment, val):
    print(env.autoescape)
    return val


env.filters["fun"] = fun
template = env.get_template("template2.html")
data = template.render(seq=["<h1>1<h1>", "<h2>2<h2>", "<h3>3<h3>"])
print(data)

html_path = pathlib.Path(__file__).parent.joinpath("test.html")

with open(html_path, mode="w", encoding="utf-8") as w_f:
    w_f.write(data)

False
False
False
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul>
         <li><h1>1<h1></li>
         <li><h2>2<h2></li>
         <li><h3>3<h3></li>
    </ul>
</body>

</html>

从结果看,默认是未开启自动转义的,因此结果的html标签是正常的,当对val添加escape之后,修改如下

@pass_environment
def fun(env: Environment, val):
    print(env.autoescape)
    return escape(val)
False
False
False
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul>
         <li>&lt;h1&gt;1&lt;h1&gt;</li>
         <li>&lt;h2&gt;2&lt;h2&gt;</li>
         <li>&lt;h3&gt;3&lt;h3&gt;</li>
    </ul>
</body>

</html>

结果发现,html标签被转义了

  • jinja2.Markup([string])
    将字符串标记为可以安全地包含在 HTML/XML 输出中,而无需转义。
    jinjia2 3.1.0之后,需from markupsafe import Markup导入,Markup可以对于字符串进行转义和不转义操作,也可以对于字符串进行一些字符串操作,例如首字母大写,是否是数字、字母等,
    如下,和上方的escape类似,当然也可以使用unescape进行不转义操作
import pathlib
from jinja2 import FileSystemLoader, pass_environment
from markupsafe import Markup

template_path = (
    pathlib.Path(__file__).parent.joinpath("template_demo").joinpath("templates")
)

env = Environment(
    trim_blocks=True, lstrip_blocks=True, loader=FileSystemLoader(template_path)
)


@pass_environment
def fun(env: Environment, val):
    print(env.autoescape)
    return Markup.escape(val)


env.filters["fun"] = fun
template = env.get_template("template2.html")
data = template.render(seq=["<h1>1<h1>", "<h2>2<h2>", "<h3>3<h3>"])
print(data)

html_path = pathlib.Path(__file__).parent.joinpath("test.html")

with open(html_path, mode="w", encoding="utf-8") as w_f:
    w_f.write(data)

异常

自定义过滤器

自定义过滤器只是常规的 Python 函数,过滤器左边作为第一个参数,其余的参数作 为额外的参数或关键字参数传递到过滤器。
例如在过滤器 {{ 42|myfilter(23) }} 中,函数被以 myfilter(42, 23) 调用, 注册过滤器一般有两种情况
**1.不依赖于上下文及环境
例如存在如下模板文件, fun为我们自定义的过滤器

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul>
        {% for item in seq %} {# test #}
        <li>{{ item|fun() }}</li>
        {% endfor %}
    </ul>
</body>

</html>

python代码
我们设置了过滤器返回值大于3的值

import pathlib
from jinja2 import FileSystemLoader


def fun(val):
    return val > 3


template_path = (
    pathlib.Path(__file__).parent.joinpath("template_demo").joinpath("templates")
)

env = Environment(
    trim_blocks=True, lstrip_blocks=True, loader=FileSystemLoader(template_path)
)
env.filters["fun"] = fun
template = env.get_template("template2.html")
data = template.render(seq=[1, 2, 3])
print(data)

结果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul>
         <li>False</li>
         <li>False</li>
         <li>False</li>
    </ul>
</body>

</html>

2.依赖上下文环境,需要上下文环境相关参数,
contextfilter是上下文过滤器,传入的第一个参数为上下文context
evalcontextfilter是环境过滤器,传入的第一个参数为eval_context
environmentfilter:是求值上下文过滤器,传入的第一个参数为environment
需要注意,python3.0之后,
pass_context 替代 contextfunctioncontextfilter.
pass_eval_context 替代 evalcontextfunctionevalcontextfilter
pass_environment 替代 environmentfunctionenvironmentfilter.
例如在设置自动转义的情况下不希望将部分标签进行转义, 使用方法如下:

import pathlib
from jinja2 import FileSystemLoader, pass_environment, pass_eval_context, pass_context


template_path = (
    pathlib.Path(__file__).parent.joinpath("template_demo").joinpath("templates")
)

env = Environment(
    trim_blocks=True, lstrip_blocks=True, loader=FileSystemLoader(template_path)
)


@pass_environment
def fun(env: Environment, val):
    print(env.block_end_string)
    print(env.filters)
    return val > 3


env.filters["fun"] = fun
template = env.get_template("template2.html")
data = template.render(seq=[1, 2, 3])
print(data)

求值上下文

求值上下文(缩写为 eval context 或 eval ctx ),2,4版本添加,就是上述自定义过滤器的evalcontextfilter,它目前主要使用于启用或关闭自动转义,官方推荐使用求值上下文来获取转义状态。

from jinja2.nodes import EvalContext

@pass_eval_context
def fun(eval_ctx: EvalContext, val):
    return eval_ctx.autoescape

自定义测试

与自定义过滤器类似,也是通过注册的方式,不过是tests关键字,一般用于模板的类型与一致性检查

原创文章,作者:wdmbts,如若转载,请注明出处:https://blog.ytso.com/tech/python/277125.html

(0)
上一篇 2022年7月26日 18:10
下一篇 2022年7月26日 18:10

相关推荐

发表回复

登录后才能评论