為什么需要模板?
- 讓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>'
延伸閱讀