Jinja2
本文全文都来自:欢迎来到 Jinja2 — Jinja2 2.7 documentation
简介
jinja2 是一个模仿 Django 模板语言而重新开发的模板引擎,因为 Django 模板引擎限制比较多,因此有人开发出了 jinja2 这个库。所以如果你比较熟悉 Django 模板,那么 jinja2 你也很快就能了解。
安装
pip install Jinja2
基本使用
from jinja2 import Template
# 使用字符串,创建一个模板。模板包含了一个变量 {{ name }}
temp = Template("<p>{{ name }}</p>")
# 给模板传递变量,并渲染
content = temp.render(name="Hello") # 可以传递关键字参数
# content = temp.render({"name": "Hello"}) # 也可以传递字典类型
print(content)
{{}}
是用来在模板中声明变量的。模板自带了一个render()
函数,可以给模板传递变量(context
,也叫上下文变量),然后渲染它的内容。
Template 对象
from jinja2 import Template
temp = Template("<p>{{ name }}</p>")
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) # 渲染后的内容
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)
来渲染模板。
该环境的过滤器字典。只要没有加载过模板,添加新过滤器或删除旧的都是安全的。自定义过滤器见 自定义过滤器 。有效的过滤器名称见 标识符的说明 。
该环境的测试函数字典。只要没有加载过模板,修改这个字典都是安全的。 自定义测试见 see 自定义测试 。有效的测试名见 标识符的说明 。
一个全局变量字典。这些变量在模板中总是可用。只要没有加载过模板,修 改这个字典都是安全的。更多细节见 全局命名空间 。有效的 对象名见 标识符的说明 。
from_string
(source, globals=None, template_class=None)从字符串加载一个模板。
get_or_select_template
(template_name_or_list, parent=None, globals=None)
select_template
(names, parent=None, globals=None)和 get_template() 很像,但是会尝试多次获取模板.如果找不到模板会抛出 TemplatesNotFound
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')
字典加载器,可以使用一个字典对象加载模板,字典的键是模板名,值是模板的文本字符串:
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="&")
env = Environment(loader=loader)
# 直接使用 app1 + delimiter + 模板名 就可以找到模板
template = env.get_template("app1&test.html") # 前提是 test.html 需要存在于 ./temp/templates 这个路径下
print(template.render(name="TEST"))
一个可选加载器,接收一个加载器列表。如果第一个加载器找不到相应的模板,则会从第二个加载器开始找,并以此类推 …
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, # &lt;p&gt;Hello world!&lt;/p&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)
消除空白
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:
-
可以不成对出现要点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 代码,让子模板继承。
子模板: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 %}
非常关键:它告诉模板要继承另一个模板。并且这个标签要放在模板的最上面。
针对一个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 过滤器,会转义字符 :&lt;script&gt;test</script> #}
""")
x = temp.render(value="<script>test</script>")
print(x)
当然,你也可以自动转义:
{% autoescape true %}
自动转义在这块文本中是开启的。
{% endautoescape %}
{% autoescape false %}
自动转义在这块文本中是关闭的。
{% endautoescape %}
控制结构
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.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)
,而我们嵌套其他模板时,也可以将它们中的上下文包含进来,这样在当前模板中也可以使用被嵌套模板中的上下文。
导入
{% 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 %}
表达式
数学计算:
+
-
*
/
//
%
譬如:
{{ 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 %}
内置过滤器和测试
内置过滤器
内置测试
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进行投诉反馈,一经查实,立即删除!