本文介绍: jinja2 是一个模仿 Django 模板语言而重新开发模板引擎,因为 Django 模板引擎限制比较多,因此有人开发出了 jinja2 这个库。所以如果你比较熟悉 Django 模板,那么 jinja2 你也很快就能了解。

Jinja2

本文全文都来自:欢迎来到 Jinja2 — Jinja2 2.7 documentation

简介

jinja2 是一个模仿 Django 模板语言而重新开发的模板引擎,因为 Django 模板引擎限制比较多,因此有人开发出了 jinja2 这个库。所以如果你比较熟悉 Django 模板,那么 jinja2 你也很快就能了解。

安装

pip install Jinja2

基本使用

from jinja2 import Template

# 使用字符串创建一个模板模板包含了一个变量 {{ name }}
temp = Template("&lt;p&gt;{{ name }}</p&gt;")

# 给模板传递变量,并渲染
content = temp.render(name="Hello")  # 可以传递关键字参数
# content = temp.render({"name": "Hello"})  # 也可以传递字典类型

print(content)

{{}} 是用来在模板中声明变量的。模板自带了一个 render() 函数可以给模板传递变量context,也叫上下文变量),然后渲染它的内容

Template 对象

模板对象, 就是我们上面用到的:

from jinja2 import Template

temp = Template("<p&gt;{{ name }}</p&gt;")
content = temp.render(name="Hello")

print(content)

比较大的模板文件,可以使用生成器,来减少内存压力:

from jinja2 import Template

temp = Template("{{ name }}")
gen = temp.generate(name="Wang")  # 返回的是生成器
for g in gen:
	print(g)  # 渲染后的内容

Template 对象常见的属性方法

TemplateStream 对象

我们对 Template 对象使用 .stream() ,就可以渲染模板并返回一个模板流。它有两个方法

Environment 对象

除了上面我们介绍的直接使用 Template 对象我们还可以使用 Environment 对象,来保存一些配置,譬如:

from jinja2 import Environment, PackageLoader, Template

# 声明一个 package 加载器,会自动temp 这个 python包下templates 文件夹找所有的文件,即:./temp/templates/*.*
loader = PackageLoader("temp", "templates")
env = Environment(loader=loader)  # 生成环境

template = env.get_template("test.html")  # 加载某个文件,会从:./temp/templates/ 路径自动查找这个 test.html

print(template.render(name="ahha"))

Environemt 的好处是,可以保存一些配置,以后所有使用这个环境获取的模板,都会统一使用这个配置。

我们可以通过 env.get_template() 来获取某个模板对象,然后通过 template_obj.render(context) 来渲染模板。

Environment 参数

Environment 对象的一些属性

Loader 加载器

环境对象这一小节中,我们用到了加载器,下面有几种不同的加载器:

FileSystemLoader(pathes, encoding=’utf-8′)

文件系统加载器,可以直接使用路径,或者路径列表作为参数,来加载这些路径下的所有模板文件:

loader =  FileSystemLoader('./temp/templates')
loader =  FileSystemLoader(['./temp/templates', './temp/others'])

PackageLoader(package_name, package_path=’templates‘, encoding=’utf-8′)

python包加载器,会从python的包(带有__init__.py)中的 templates 文件夹下加载所有的模板:

loader = PackageLoader('package1', 'templates')

DictLoader(mapping)

字典加载器,可以使用一个字典对象加载模板,字典的键是模板名,值是模板的文本字符串:

from jinja2 import Environment, DictLoader

loader = DictLoader({'index.html': 'source {{ name }} here'})
env = Environment(loader=loader)
template = env.get_template("index.html")

print(template.render(name="TEST"))

PrefixLoader(mapping, delimiter=’/’)

一个前缀加载器,接收一个字典,字典的键是前缀,字典的值是一个加载器,之后就可以使用 前缀+delimiter+模板名 来加载模板:

from jinja2 import Environment, PackageLoader, PrefixLoader


loader = PrefixLoader({
	'app1': PackageLoader('temp', "templates")
}, delimiter="&amp;")

env = Environment(loader=loader)

# 直接使用 app1 + delimiter + 模板名 就可以找到模板
template = env.get_template("app1&amp;test.html")  # 前提是 test.html 需要存在于 ./temp/templates 这个路径print(template.render(name="TEST"))

ChoiceLoader(loaders)

一个可选加载器,接收一个加载器列表。如果第一个加载器找不到相应的模板,则会从第二个加载器开始找,并以此类推 …

from jinja2 import Environment, ChoiceLoader, FileSystemLoader

loader = ChoiceLoader([
    FileSystemLoader('./'),  # 这个路径下没有模板
    FileSystemLoader('./temp/templates')  # 这个路径有模板:test.html
 ])

env = Environment(loader=loader)

template = env.get_template("test.html")  # 依然能找到

print(template.render(name="TEST"))

转义

为了安全起见,所有用户输入文本,都应该进行转义,因为用户可能输入不安全的 html 字符,从而进行 跨站脚本 攻击(Cross Site Scripting)

from markupsafe import Markup

s = "<p>Hello world!</p>"   # html 字符串
m = Markup(s)               # markup 对象

t = m.striptags()  # 清除标签,只剩下文本
e = Markup.escape(s)  # 转义特殊字符
o = e.unescape()  # 特殊字符重新转义回文print(
	m,  # <p>Hello world!</p>
	t,  # Hello world!
	e,  # &amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;gt;
	o,  # <p>Hello world!</p>

	sep='n'
)

过滤

过滤器就是 python 函数,只不过它可以用特殊方式,在模板中使用,渲染模板的时候,会自动执行这个函数。

过滤器的语法糖是:|

譬如:

<p>
    {{ 40|addFilter(30) }}
</p>

假设我们有一个叫做 addFilter 的特殊过滤器函数,上面的代码调用 addFilter(40, 30) 然后将返回值渲染到页面上。

from jinja2 import Environment


# 一个普通函数
def addFilter(x, y):
	return x + y


env = Environment()
env.filters['addFilter'] = addFilter  # 添加一个过滤器

con = env.from_string("""
{{ 40|addFilter(30) }}
""").render()
print(con)

测试

所谓测试,其实就是判断语句比如 python如下代码:

x = 2
if x == 2:
    return True
else:
    return False

jinja2 示例

from jinja2 import Environment, FileSystemLoader, Template
from jinja2.nodes import EvalContext

# 一个普通的函数
def is_odd(n):
	if n % 2 == 0:
		return False
	else:
		return True


env = Environment()
# 给环境添加一个自定义的测试
env.tests["odd"] = is_odd

# 从字符串加载一个模板
temp = env.from_string("""
	<p>
		{% if 3 is odd %}   <!-- 这是一个代码块,odd 不是一个普通字符串,而是 odd 测试函数; '3 is odd',相当于执行: is_odd(3) -->
			<span>3 is odd</span>
		{% else %}
			<span>3 is not odd</span>
		{% endif %}
	</p>
""")

# 渲染模板,返回内容
content = temp.render()
print(content)

模板语法

注释

在模板中,注释使用 {# ... #} 表示:

<p>
    {# this is comment,
        and this is comment too.
    #}
</p>

变量

变量在模板中的语法,用 {{ 和 }} 括起来:

<p>
    {{ name }}  <!-- name 就是一个变量 -->
    {{ obj.name }}  <!-- 提取 obj 对象的属性 -->
    {{ obj["name"] }}  <!-- 和 obj.name 等效 -->
</p>

针对上述模板,我们后台如下

from jinja2 import Template

class Obj:
	name = "wang"

temp = Template("""<p>
    {{ name }}  <!-- name 就是一个变量 -->
    {{ obj.name }}  <!-- 提取 obj 对象的属性 -->
    {{ obj["name"] }}  <!-- 和 obj.name 等效 -->
</p>""")

gen = temp.render(obj=Obj(), name="Fake")
print(gen)

可以看出,我们可以像是使用普通的 python 语法一样,在模板中提取属性或者字典的值

消除空白

jinja2 会严格按照模板渲染,也就是说,如果你的模板中写入了空格,或者在标签之间换行了,渲染的内容也会原封不动的换行

譬如:

from jinja2 import Template

class Obj:
	name = "wang"

temp = Template("""<p>         {# <p>后面有个换行符 #}
    {{ name }} {{ obj.name }}  {# 这两个变量在一行  #}
    {{ obj["name"] }}          {# 后面也有换行符 #}
</p>""")

gen = temp.render(obj=Obj(), name="Fake")
print(gen)

会渲染成:

<p>
    Fake wang
    wang
</p>

如果你想将三个变量和标签都显示一行,只能这样:

temp = Template("""<p>{{ name }}{{ obj.name }}{{ obj["name"] }}</p>""")

会渲染成:

<p>Fakewangwang</p>

如果我们想要在模板中好看(模板中换行),但是实际渲染的效果要在一行,可以使用 - 符号

譬如:

temp = Template("""<p>
	{{- name -}}
 	{{- obj.name -}} 
	{{- obj["name"] -}}
</p>""")

会渲染成:

<p>Fakewangwang</p>

要点1:- 可以不成对出现

要点2:- 和 {{ 或 }} 之间没有空格

要点3:{{- 代表消除变量之前的空白符,-}} 代表消除变量之后的空白符。

要点4:- 不仅可以用在 {{ .. }} 上,也可以用在 {% .. %} 上

转义自身语法

如果你想要转义 {{ 本身,可以使用:

{{ '{{' }}

对于较大的段落,可以使用 raw 来将里面内容全部当作原生字符

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

语句

我们之前曾经提到过行语句。其实就是自定义一个符号,然后在模板中,所有以这个符号开头的字符串,都会被当作语句来执行,譬如:

from jinja2 import Template


s = """
<p>
	# for i in [
		'a', 
		'b',
		'c'
	]:
		{{ i }}
	# endfor
</p>
"""
temp = Template(s, line_statement_prefix="#")  # 以 # 作为语句定义符号

gen = temp.render()
print(gen)

上面的以 # 开头的行,会作为语句执行,并且语句结尾可以加冒号,并且如果遇到[],()等,可以换行

模板 block

编程语言继承概念,模板也可以有。我们可以写一个基本模板,然后让子模板继承这个模板

基本模板:

mother.html

{% block title %}           {# 声明一个名为 title 的block #}
    <p>This is title</p>
    {% block content %}     {# title 内部嵌套了一个名为 content 的block #}
    {% endblock %}
{% endblock %}

{% block foot %}
    <span>This is foot</span>
{% endblock %}

上面我们编写了一个母版,它里面定义了很多的 block, 每个 block 都有自己名字(block的名字不能重复): {% block blok_name %}...{% endblock %} ,在 block 中,我们可以写入一些 html 代码,让子模板继承

各个 block 之间是可以嵌套

注意每个 block 要有一个 {% endblock %}

子模板:son.html

{% extends "mother.html" %}             {# 继承母版 #}

{% block content %}                     {# 重写某个block #}
    <span>This is content, and the mother.html doesn't have this.</span>
{% endblock %}

{% block foot %}
    {{ super() }}                       {# 继承母版中的 foot block 的内容 #}
    <span>New foot content</span>
{% endblock %}

{% extend %} 非常关键:它告诉模板要继承另一个模板。并且这个标签要放在模板的最上面。

当然,继承的标签可以写路径: {% extends "layout/default.html" %}

如果子模板没有重写母版中的某个block,则会默认使用母版中的block。

命名 block 的结束标签

针对一个block,我们还可以在 endblock 时写上它的名字,当然像上面的例子一样不写也行。

{% block sidebar %}
{% endblock sidebar %}

作用域

一个 block 的内容,无法和block外部的内容互动,它有自己作用域。譬如,你想在一个 for 循环循环某个block,而block却无法获取for循环作用域

{% extends "mother.html" %}             {# 继承母版 #}

{% for i in [1,2,3] %}
    {% block foot scoped %}  			{# 后面加了一个 scoped, 就可以获取 for 循环中的变量了 #}
        {{ i }}
    {% endblock %}
{% endfor %}

转义字符

from jinja2 import Template


temp = Template("""
{{ value|safe }}    {# safe 是一个过滤器,不转义字符 :<script>test</script> #}
{{ value|e }}		{# e 过滤器,会转义字符 :&amp;lt;script&amp;gt;test&lt;/script&gt; #}
""")

x = temp.render(value="<script>test</script>")
print(x)

当然,你也可以自动转义:

{% autoescape true %}
	自动转义在这块文本中是开启的。
{% endautoescape %}

{% autoescape false %}
	自动转义在这块文本中是关闭的。
{% endautoescape %}

控制结构

For

for 可以用来遍历序列,如列表,字典等。

{% for item in items %}   <!-- 类似 for 这种代码块里面用到的变量,不需要额外加 {{}}, 譬如 items 就是一个变量 -->
	{{ item }}
{% endfor %}


{% for key, value in my_dict.items() %}
	{{ key }}
{% endfor %}


{% for i in [0, 1, 2] if not i %}
	{{ i }}
{% else %}							{# else 会在 for 循环没有成功执行的情况下执行 #}
	<span>List is empty</span>
{% endfor %}

查看循环的索引(循环到第几个元素了)

from jinja2 import Template


temp = Template("""
{% for key, value in my_dict.items() %}
	{{ key }}
	{{ loop.index }}    {# loop 是 jinjia2 的一个特殊对象,可以获取当前循环的索引位置 #}
{% endfor %}
""")

x = temp.render(my_dict={"a":"b", "b":"a"})
print(x)

loop 的几个特殊属性

变量 描述
loop.index 当前循环迭代的次数(从 1 开始)
loop.index0 当前循环迭代的次数(从 0 开始)
loop.revindex 到循环结束需要迭代的次数(从 1 开始)
loop.revindex0 到循环结束需要迭代的次数(从 0 开始)
loop.first 如果是第一次迭代,为 True 。
loop.last 如果是最后一次迭代,为 True 。
loop.length 序列中的项目数。
loop.cycle 在一串序列间期取值的辅助函数。见下面的解释
from jinja2 import Template


temp = Template("""
{% for i in [1,2,3] %}
	{{ loop.cycle('A', "C", "B") }}  {# loop.cycle会循环执行里面的A-C-B #}
{% endfor %}
""")

x = temp.render()
print(x)

If

python中的if一样:

{% if x %}
	...
{% elif y %}
	...
{% else %}
	...
{% endif %}

跳出循环

continue, break

import jinja2.ext
from jinja2 import Template


temp = Template("""
{% for i in [1,2,3] %}
	{% if i == 1 %}
		{{ i }}
		{% continue %}
	{% else %}
		{% break %}
	{% endif %}
{% endfor %}
""", extensions=[jinja2.ext.loopcontrols])  # 要额外加载一个扩展,才能使用 continue 和 break

x = temp.render()
print(x)

宏类似于函数。我们可以定义一个宏,然后定义宏的内容。以后我们可以像调用函数一样调用宏。

{# 声明了一个名为 input 的宏,它还带有几个参数 #}
{% macro input(name, value='', type='text', size=20) -%}
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}">
{%- endmacro %}

{# 调用宏,并传参 #}
<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>

include

include 可以直接将另一个模板包含进当前模板,相当于将另一个模板直接嵌套进来。

{% include 'header.html' %}

{% include "sidebar.html" ignore missing %}  			{# ignore missing:如果找不到模板,可以忽略 #}

{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}  {# 可以导入列表 #}

{% include "sidebar.html" ignore missing without context %}            {# without context 可以不携带上下文 #}

{% include "sidebar.html" ignore missing with context %}            {# with context 可以携带上下文 #}

什么是上下文:

上下文其实就是模板中定义的变量,我们渲染时会将上下文传递给模板:template.render(context) ,而我们嵌套其他模板时,也可以将它们中的上下文包含进来,这样在当前模板中也可以使用被嵌套模板中的上下文。

导入

假设现有:forms.html ,定义两个

{% macro input(name, value='', type='text') -%}
    <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}

{%- macro textarea(name, value='', rows=10, cols=40) -%}
    <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
        }}">{{ value|e }}</textarea>
{%- endmacro %}

我们可以在其他模板中,像导入模块一样导入它:

{# 导入整个模块 #}
{% import 'forms.html' as forms %}
<dl>
    <dt>Username</dt>
    <dd>{{ forms.input('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>


{# 也可使用 from .. import .. as . 来单独导入模板中的宏 #}
{% from 'forms.html' import input as input_field, textarea as txt_field %}
<dl>
    <dt>Username</dt>
    <dd>{{ input_field('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ input_field('password', type='password') }}</dd>
</dl>
<p>{{ txt_field('comment') }}</p>

还可以导入时带入上下文:

{% from 'forms.html' import input with context %}

表达式

在模板中,可以正常使用python中常见的表达式

数学计算

+ 
-
*
/
//
%

譬如:

{{ 1 + 2 }}

字面量:

dict
list
tuple
str

true
false

譬如:

<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'),
                         ('downloads.html', 'Downloads')] %}
    <li><a href="{{ href }}">{{ caption }}</a></li>
{% endfor %}
</ul>

或者:

{% for key in {‘dict’: ‘of’, ‘key’: ‘and’, ‘value’: ‘pairs’} %}
    ...
{% endfor %}

比较运算

==
>=
<=
!=
>
<

逻辑运算

and
or
not


is   # 用于测试
in
|    # 用于过滤器
()   # 调用函数
./[] # 用来获取对象的属性

譬如:

{% if 1 in [1, 2, 3] and 2==2 %}
   ...
{% endif %}


内置过滤器和测试

内置过滤器

abs() float() lower() select() truncate()
attr() forceescape() map() selectattr() upper()
batch() format() pprint() slice() urlencode()
capitalize() groupby() random() sort() urlize()
center() indent() reject() string() wordcount()
default() int() rejectattr() striptags() wordwrap()
dictsort() join() replace() sum() xmlattr()
escape() last() reverse() title()
filesizeformat() length() round() tojson()
first() list() safe() trim()

内置测试

callable() escaped() lessthan() number() string()
defined() even() lower() odd() undefined()
divisibleby() greaterthan() mapping() sameas() upper()
equalto() iterable() none() sequence()

原文地址:https://blog.csdn.net/xuezhangjun0121/article/details/128497276

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_17127.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注