20190118 Jinja2模板详解
为什么需要模板?
- 让HTML设计者和后端Python开发工作分离
- 减少使用PYthon的复杂程度,页面逻辑应该独立业务逻辑,这样才能开发出易于维护的程序
- 模板非常灵活、快速和安全,对设计者和开发者会更友好
Python语言自带的模板
In : from string import Template
In : s = Template('$who likes $what')
In : s.substitute(who='tim', what='kung pao')
Out: 'tim likes kung pao'
In : s = Template("$var is here but $missing is not provided")
In : s.safe_substitute(var='tim')Out: 'timis here but $missing isnot provided'
In : class MyTemplate(Template):
...: delimiter = '@' # 使用`@`为分隔符
...: idpattern = '[a-z]+\.[a-z]+' # 符合的模式才会被替换
...:
In : t = MyTemplate('@with.dot @notdoted')
In : t.safe_substitute({'with.dot': 'replaced', 'notdoted': 'not replaced'})
Out: 'replaced @notdoted'
Jinja2特点
- 沙箱中执行
- 强大的HTML自动转移系统保护系统免受XSS攻击
- 模板继承
- 即时编译最优的Python代码
- 易于调式
- 可配置的语法
安装Jinja2
❯ pip install Jinja2❯
python -c 'import jinja2; print(jinja2.__version__)'2.10
基本API使用
In : from jinja2 import Template
In : template = Template('Hello {{ name }}!')
In : template.render(name='Xiao Ming')
Out: u'Hello Xiao Ming!'
In : from jinja2 import EnvironmentIn : env = Environment()
In : template = env.from_string('Hello {{ name }}!')
In : template.render(name='Xiao Ming')
Out: u'Hello Xiao Ming!'
❯ echo "Hello {{ name }}" > hello.html❯
touch app.py
❯ ipython
In : from jinja2 import Environment, PackageLoader
In : env = Environment(loader=PackageLoader('app', 'templates'))
In : template = env.get_template('hello.html')
In : template
Out: <Template 'hello.html'>
In : template.render(name='Geng WenHao')
Out: u'Hello Geng WenHao'
Jinja2支持多种loader
- FileSystemLoader
In : from jinja2 import FileSystemLoader
In : loader = FileSystemLoader('templates')
In : template = Environment(loader=loader).get_template('hello.html')
In : loader = FileSystemLoader(['/path/to/templates', '/other/path'])
- DictLoader
In: loader = DictLoader({'hello.html': 'Hello {{ name }}'})
- 自定义Loader
❯ cat my_loader.py
# coding=utf-8
from os.path import join, exists, getmtime
from jinja2 import BaseLoader, TemplateNotFound
classMyLoader(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 file(path) as f:
source = f.read().decode('utf-8')
return source, path, lambda: mtime == getmtime(path)
字节码缓存 - FileSystemBytecodeCache
from jinja2 import FileSystemBytecodeCache
bcc = FileSystemBytecodeCache('/tmp/jinja2_cache', '%s.cache')
字节码缓存 - MemcachedBytecodeCache
from jinja2 import MemcachedBytecodeCache
import memcache
mc = memcache.Client(['127.0.0.1:11211'], debug=0)
bxx = MemcachedBytecodeCache(mc, prefix='jinja2/bytecode/')
自定义字节码缓存
❯ mkdir tmp # 创建一个存放字节码缓存的目录, 之后的临时文件也都放在这里面
❯ cat bytecode_cache.py
# coding=utf-8
import shutil
from os import path
from jinja2 import Environment, DictLoader, BytecodeCache
class MyCache(BytecodeCache):
def __init__(self, directory):
self.directory = directory
def load_bytecode(self, bucket):
filename = path.join(self.directory, bucket.key)
if path.exists(filename):
with open(filename, 'rb') as f:
bucket.load_bytecode(f)
def dump_bytecode(self, bucket):
filename = path.join(self.directory, bucket.key)
with open(filename, 'wb') as f:
bucket.write_bytecode(f)
def clear(self):
shutil.rmtree(self.directory)
if __name__ == '__main__':
loader = DictLoader({'hello.html': 'Hello {{ name }}'})
env = Environment(loader=loader, bytecode_cache=MyCache('tmp'))
template = env.get_template('hello.html')
print(template.render(name='Geng WenHao'))
❯ python bytecode_cache.pyHello Xiao Ming
❯ ll tmptotal 4.0K-rw-r--r-- 1 dongwm staff 870 Sep 1516:349a60b6907b5908143cfea0aa8f6f88cd6b9138df
自定分隔符
In : template = Template('Hello $ name $!', variable_start_string='$', variable_end_string='$') # 现在使用`$`作为分隔符
In : template.render(name='Geng WenHao')
Out: u'Hello Geng WenHao!
''''
* block_start_string:块开始标记。默认是{%。
* block_end_string:块结束标记。默认是%}。
* comment_start_string:注释开始标记。默认是{#。
* comment_end_string:注释结束标记。默认是#}。
'''
沙箱
In : from jinja2.sandbox import SandboxedEnvironment
In : env = SandboxedEnvironment()
In : env.from_string("{{ func.func_code }}").render(func=lambda:None)
Out: u''
In : env.from_string("{{ func.func_code.do_something }}").render(func=lambda:None)
--------------------------------------------------------------------SecurityError
JinJa2的基本语法
> cat simple.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Simple Page</title>
</head>
<body>{# This is a Comment #}
<ul id="navigation">
{% for item in items %}
<li><a href="{{ item.href }}">{{ item['caption'] }}</a></li>
{% endfor %}
</ul>
<h1>{{ title | trim }}</h1>
<p>{{ content }}</p>
</body>
</html>
空白控制
{% for item in seq -%}
{{ item }}
{%- endfor %}
{%- if foo -%}...{% endif %} # 有效的
{% - if foo - %}...{% endif %} # 无效的
行语句
<ul>
# for item in seq:
<li>{{ item }}</li>
# endfor
</ul>
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<ul># for href, caption in [('index.html', 'Index'), ('about.html', 'About')]:
<li><a href="{{ href }}">{{ caption }}</a></li>
# endfor
</ul>
# for item in seq:
<li>{{ item }}</li> ## this comment is ignored
# endfor
模板继承
❯ cat base.html
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet"href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">
{% block content %}
{% endblock %}
</div>
<div id="footer">
{% block footer %}
{% endblock %}
</div>
</body>
</html>
❯ cat index.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">.important { color: #336699; }
</style>
{% endblock %}{% block content %}
<h1>Index</h1>
<p class="important">
Welcome on my awesome homepage.
</p>
{% endblock content %}
控制结构 - for
{% for item in items %}
...
{% endfor %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% else %}
<li><em>no users found</em></li>
{% endfor %}
</ul>
# 特殊变量
1.loop.index:当前循环迭代的次数(从1开始)
2.loop.index0:当前循环迭代的次数(从0开始)
3.loop.first:如果是第一次迭代值为True
4.loop.last:如果是最后一次迭代值为True
5.loop.length:序列中的项目数
控制结构 - 条件语句
{% if xiaoming.is_admin %}
GengWenHao is a Administrator
{% elif xiaoming.is_editor %}
GengWenHao is a Editor
{% else %}
Unknown User
{% endif %}
{% extends layout_template if layout_template is defined else 'master.html' %}
宏
In : Template('''
...: {% macro hello(name) %}
...: Hello {{ name }}
...: {% endmacro %}
...: <p>{{ hello('world') }}</p>
...: ''').render()
Out: u'\n\n<p>\n Hello world\n</p>'
赋值
In : print Template('''
...: {% set a = 1 %}
...: {% set b, c = range(2) %}
...: <p>{{ a }} {{ b }} {{ c }}</p>
...: ''').render()
Out: u'\n\n\n<p>1 0 1</p>'
include
{% include 'header.html' %}
Body
{% include 'footer.html' %}
{% include "sidebar.html" ignore missing %}
import
❯ cat macro.html
{% macrohello(name) %}
Hello {{ name }}
{% endmacro %}
{% macro strftime(time, fmt='%Y-%m-%d %H:%M:%S') %}
{{ time.strftime(fmt) }}
{% endmacro %}❯
> cat hello_macro.html
{% import 'macro.html'asmacro%}
{% from 'macro.html' import hello as _hello, strftime %}
<p>{{ macro.hello('world') }}</p>
<p>{{ strftime(time) }}</p>
In : from jinja2 import FileSystemLoader, Environment
In : from datetime import datetime
In : loader = FileSystemLoader('templates')
In : template = Environment(loader=loader).get_template('hello_macro.html')
In : template.render(time=datetime.now())
Out:'\n\n\n<p>\n Hello world\n</p>\n<p>\n 2018-09-15 23:05:19\n</p>'
延伸阅读