在以前的文章中,简单介绍了Python Web开发框架Flask,知道了如何写个Hello World,可是距离用Flask开发真正的项目,还有段距离,如今咱们目标更靠近一些 —— 学习下Jinja2模板。html
模板是用来作什么的呢?模板是用来更高效地生成相应时的Html文本的,没有模板,能够手写,好比以前的hello world示例,写段html代码:python
<h1>Hello world!</h1>
对于简单的练习还行,但对于规模大的,动态化程度高的项目来讲,这样写就有些勉强了,即,不利于项目和产品化。那么模板有什么好处呢:linux
能让展示逻辑和业务逻辑 展现逻辑即UI,就是用来给用户看和操做的,业务逻辑是业务规则,好比什么条件能够注册,什么权限能考到什么。模板将展示逻辑封装起来,业务逻辑写在视图函数中。git
能使项目更易维护 因为展示逻辑和业务逻辑的分离,它们能够由不一样的开发人员来维护,不会有代码冲突的问题程序员
使项目更加安全 在作交互式开发中,有个原则: 永远不要相信用户的输入,由于恶意用户可能经过输入来注入(关于注入之后有机会能够单独聊聊),而模板在必定程度上会防注入,例如用户输入一点html代码做为输入,默认状况下模板会将其替换为网络安全字符,以防止恶意注入。github
能提升开发效率 有了模板,至关于一个展现逻辑的函数,因此就能够被复用,能够用在不一样的视图函数中,也能够用在不一样的项目中面试
思考下:上面提到的展示逻辑和业务逻辑,为何不直接说成前台和后台呢? 若是你有答案和想法,欢迎留言讨论。编程
Jinja2是Flask框架默认支持的模板引擎,并非惟一也不是最好(因人而异,没有最好)模板引擎,不一样的Web框架,好比Django、Nodejs等都有本身的模板引擎,甚至一些程序员本身实现的模板引擎(我就这么干过),但大致思路是同样的,都是要将数据替换或者转换到,用特殊格式标记了位置的模板中,以合成动态的html,这种技术不新鲜,在以前的打印模板,如水晶报表里就有,无非就是标记和语法不一样而已,因此要触类旁通。flask
像其余功能同样,要使用模板引擎,先引入api
from flask import render_template
注意:要将将模板文件放置在项目根目录(即
print(__file__)
显示的路径)下的templates
文件夹中
例如模板文件 hello.html
为:
<h1>Hello {{ name }} </h1>
{% endraw %}
视图函数能够写成:
@app.route('/user/<name>')def index(name): return render_template('hello.html', name=name)
Flask提供的 render_template
函数把Jinja2模板引擎集成到了程序中。render_template
函数第一个参数是模板的文件名,随后的参数都是键值对,表示模板中变量的对应的真实值,在上面代码中,模板会接收到一个名为 name
的变量
模板文件就是普通的文本文件,而后将须要替换的部分用双大括号( {{}}
)标记出来,双大括号中,表示要替换的变量名,这个变量支持基本数据类型,以及列表、词典、对象和元组。如模板 template.html
:
<p> A value form a string: {{ name }}.</p><p> A value form a int: {{ myindex }}.</p><p> A value form a list: {{ mylist[3]] }}.</p><p> A value form a list, with a variable index: {{ mylist[myindex] }}.</p><p> A value form a dictionary: {{ mydict['key'] }}.</p><p> A value form a tuple: {{ mytuple }}.</p><p> A value form a tuple by index: {{ mytuple[myindex] }}.</p>
{% endraw %}
视图函数代码:
@app.route('/template/')def template(): name = 'Jinja2 模板引擎' myindex = 1 mylist = [1,2,3,4] mydict = { key: 'age', value: '25' } mytuple = (1,2,3,4) return render_template('template.html', name=name, myindex=myindex, mylist=mylist, mydict=mydict, mytuple=mytuple)
显示结果:
有些时候须要对要在模板中替换的值作一些特殊处理,好比首字母大写,去掉先后空格等等,有种选择就是使用过滤器。
Jinjia2模板引擎中,过滤器相似于Linux命令中的管道,例如将字符串变量的首字母大写
{% raw %}
<h1>{{ name | capitalize}}</h1>
{% endraw %}
过滤器能够拼接,和linux的管道命令同样,如对值进行所有变大写,而且去除先后空白字符:
{% raw %}
<h1>{{ name | upper | trim }}</h1>
{% endraw %}
如上代码,过滤器和变量之间用管道符号 | 相连,至关于对变量值做进一步加工。
一些经常使用的过滤器
过滤器 | 说明 |
---|---|
safe | 渲染是不转义 |
capitalize | 首字母大写 |
lower | 全部字母小写 |
upper | 全部字母大写 |
title | 值中每一个单词首字母大写 |
trim | 删除首位空白字符 |
striptags | 渲染时删除掉值中全部HTML标签 |
注意:safe过滤器,默认状况下,处于安全考虑,Jinja2会转义全部变量,例如一个变量的值为 <h1>Hello</h1>
, Jinja2会将其渲染成 <h1>Hello</>
,浏览器会显示出本来的值,可是不会解释。若是须要浏览器解释的话,可使用 safe 过滤器 例如模板文件 html.html
为:
{% raw %}
<h1>{{ html | safe }}</h1>
{% endraw %}
视图函数为:
@app.route('/html')def html(): return render_template('html.html', html='<b>bob</b>')
注意:千万别在不可信的值上使用 safe 过滤器,例如用户在表单上输入的文本。
还有一些有用的过滤器
default
,能够当变量未定义时,提供默认值,若是想将 false
、 False
和空( none
)视为未定义,须要提供第二个参数为 true
{% raw %}
{% endraw %}
当变量 name
的未定义时,上下两个显示效果同样,当值为 none
时,上面会显示 Hellonone!
, 而下面的会显示 Helloworld!
<!-- 提供默认值过滤器 -->
<h1>Hello {{ name | default('world') }}!</h1>
<!-- 将false、False和空(none)视为未定义的默认值过滤器 -->
<h1>Hello {{ name | default('world', true)! }}</h1>
列表过滤器 min
, max
, 获得列表中的最小值或最大值
过滤器虽然有不少,但总有不知足需求的时候,例如首行文字缩进、将金额转化为中文的大写等等。过滤器实质就是个函数,因此,第必定义一个过滤器函数,第二,注册到Jinjia2的过滤器中。
# 定义过滤器函数
def mylen(arg):# 实现一个能够求长度的函数
return len(arg)
def interval(test_str, start, end): # 返回字符串中指定区间的内容
return test_str[int(start):int(end)]
# 注册过滤器
env = app.jinja_env
env.filters['mylen'] = mylen
env.filters['interval'] = interval
# 视图函数
@app.route('/myfilter')
def myfilter():
return render_template('myfilter.html', phone='13300000000')
模板文件
{% raw %}
<h1>电话号码是:{{ phone }}, 长度为:{{ phone | mylen }},运营商号:{{ phone | interval(0,3) }}</h1>
{% endraw %}
过滤器注册代码还能够写在初始化代码
__init__.py
中
不少时候,须要更智能的模板渲染,即能给渲染编程,好比男生一个样式,女生同样样式,控制结构指令须要用指令标记来指定,下面介绍下一些简单的控制结构
即在模板中用 if-else
控制结构
{% raw %}
{% if gender=='male' %} Hello, Mr {{ name }}{% else %} Hello, Ms {{ name }}{% endif %}
{% endraw %}
视图函数
@app.route('/hello2/<name>/<gender>')def hello2(name, gender): return render_template('hello2.html', name=name, gender=gender)
在控制结构里,代码语法同 python
循环对于渲染列表,颇有帮助,循环的标记是 for
。例如奖列表的内容显示在 ul
中
{% raw %}
<ul>{% for name in names %} <li>{{ name }} </li>{% endfor %}</ul>
{% endraw %}
例如给定一个学生列表,将其用无序列表 ul
显示出来
模板中能够定义宏,至关于定义了一个函数,能够重复使用,让逻辑更清晰。首先,定义一个宏:
mymacro.html {% raw %}
{% macro render_name(name) %} <li>{{ name }}</li>{% endmacro %}
{% endraw %} 而后使用宏, 例如将循环结构的例子中,显示名称的地方,改成调用宏 {% raw %}
<ul> {% for name in names %} {{ render_name(name) }} {% endfor %}</ul>
{% endraw %}
调用宏,和调用函数是同样的,不过要将代码写在 {{}}
双大括号内。通常咱们会将宏存在单独的文件中,以便复用,在须要用到宏的地方,引用就行了
{% raw %}
{% import 'mymarco.html' as macros %}<ul> {% for name in names %} {{ macros.render_name(name) }} {% endfor%}</ul>
{% endraw %}
如上所述,用improt引入宏定义文件,经过as指定别名,和python的模块引入同样。指定别名是一个良好的编程习惯,能够将一个复杂的东西形象化,同时像一个命名空间同样,有效的避免冲突。
另外能够将多个模板片断写入一个单独文件,再包含( include
)在全部模板中,以提升开发效率:
{% raw %}
{% include 'common.html' %}
{% endraw %}
include
进来的文件,至关于将文件中的内容复制到 include
的位置,因此自使用以前须要考虑仔细
若是以为 include
过于呆板,灵活性差,Jinja2模板引擎还有更高级的功能——继承。相似于Python代码中类的继承,一块儿看看。首先定义一个基类, base.html: {% raw %}
<html><head> {% block head %} <title>{% block title %}{% endblock%} - My Application</title> {% endblock %}</head><body> {% block body %} <h3>这是基类的内容</h3> {% endblock %}</body></html>
{% endraw %}
其中的 block
标签,定义了能够被子类重构(替换)的部分,每一个 blcok
标签,须要指定一个特殊的名称,例如 head
、 title
等,以便子类用特定的名称来重构。另外 block
标签须要有结束标签 endblock
,相似于类C语言中的大括号,固然 block
标签也能够嵌套。接下来,定义一个子类模板 hello3.html:
{% raw %}
{% extends "base.html" %}{% block title %}Index{% endblock %}{% block head %} {{ super() }} <style></style>{% endblock %}{% block body %} {{ super() }} <h3>这是子类的内容 Hello world!</h>{% endblock %}
{% endraw %}
效果如图所示:
经过 extends
标记来指定须要继承的基类,而后用 block
标记来设置子类须要替换调基类中的内容,只要 block
指定的名称同样就行。另外,如不须要彻底替换调基类的内容,能够在子类 block
中,调用 super
方法,以获取基类在此名称下的内容,这样就能达到更号的灵活性。
今天介绍了Jinja2模板引擎的基本用法和特色,指望经过不一样的特色,让你了解到模板的基本用法,以便更快的使用和进一步学习更深刻的内容。另外,想经过Jinja2模板引擎,说明模板的基本特征,以便举一反三、触类旁通,更快的学习其余优秀的模板, 同时也想说明,模板不只仅能够用在Web的开发中,还能够用在自动化编码、测试等众多领域。最后在本章开头,留了个思考题,为何不将展示逻辑和业务逻辑说成是前台和后台呢?若是你有答案,欢迎留言交流。
图书: Flask Web开发 https://item.jd.com/12418677.html
API-Jinja Documentation(2,10.x) https://jinja.palletsprojects.com/en/2.10.x/api/#the-context
ansible基础-Jinjia2模板——过滤器
https://www.cnblogs.com/mauricewei/p/10056379.html
参考代码
https://github.com/JustDoPython/python-100-day/tree/master/day-020
系列文章