摘要:Flask
,Jinja2
,HTML
Flask模板引擎Jinja2简述
模板实质上是一个静态的包含HTML
语法的全部或片段的文本文件,也可以包含变量
表示的动态部分
。使用真实值替换网页模板中的变量,生成对应数据的HTML片段,这一过程称为渲染,Falsk使用Jinja2模板引擎来渲染模板,简单而言Jinja2就是一种基于Python的在HTML代码中对动部分进行一系列操作的语法。
Flask中使用render_template
来渲染模板,render_template的首个入参是模板文件的名称,Falsk会从项目根目录
下的templates目录
下查找模版。先创建一个first.html
这是一个测试
这是一个测试
在视图函数中使用render_template渲染模板
from flask import render_template
@app.route('/template_test')
def template_test():
return render_template('first.html')
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000)
向模板中传递参数
Flask可以将程序中的参数或变量传入指定的模板进行渲染,在HTML文件中使用{{ }}
将程序中指定的参数进行传入。修改first.html
这是一个测试
这是一个测试 {{ name }}
在渲染方法render_template中传入参数,指定模板中动态变量的名字等于程序中的参数名,必须指定模板中的动态变量名
@app.route('/template_test')
def template_test():
my_name = "你好"
return render_template('first.html', name=my_name)
传入多个变量可以直接使用**locals()
方法,此时模板中动态变量的名称要和程序中的参数名称一致,如下修改视图函数
@app.route('/template_test')
def template_test():
name = "你好"
return render_template('first.html', **locals())
Jinja2控制语句
Jinja2中的控制语句用来控制渲染的方向
,使用if
,for
关键字,放在{%%}
使用
if控制语句
if控制语句的基础语法
{% if condition %}
do something
{% else %}
do something
{% endif %}
编写一个HTML其中通过判断变量name在程序中是否存在来呈现不同的展示结果
这是一个测试
{% if name %}
存在name
{% else %}
不存在name
{% endif %}
在视图函数中将name注释掉,此时不存在name程序变量,返回页面结果为"不存在name"。
@app.route('/template_test')
def template_test():
# name = "你好"
return render_template('first.html', **locals())
多个分支条件判断
这是一个测试
{% if age == 1 %}
age为1
{% elif age == 2 %}
age为2
{% else %}
age不为1和2
{% endif %}
组合判断逻辑,支持python and or
这是一个测试
{% if age >= 1 and age < 3 %}
age为[1, 3)
{% elif age >= 3 and age < 4 %}
age为[3, 4)
{% else %}
age为其他
{% endif %}
可以在html标签中使用jinja2控制语句,实现不同条件不同样式的按钮
{% for cate in category_list %}
{% endfor %}
for控制语句
for控制语句的基础语法
{% for 目标 in 对象 %}
do something
{% endfor %}
Jinja2的for循环变量不需要{{ }}传入,不支持continue和break,但是和Python一样可以对Python的可迭代对象进行循环遍历。在一个循环代码块内部调用loop
的属性可以获得循环中的状态数据,比如可以一边循环一边拿到下标,包括
loop.index: 当前迭代的索引(从1开始)
loop.index0: 当前迭代的索引(从0开始)
loop.first: 是否是第一次迭代,返回True,False
loop.last: 是否是最后一次迭代,返回True,False
loop.length: 返回序列长度
在HTML中构建一个for循环语句,每一次循环的对象是一个字段,使用.key直接拿到value值
这是一个测试
{% for good in goods %}
{{ good.name }}
{{ good.price }}
{% endfor %}
在视图函数中创建一个字典对象传入模板
@app.route('/template_test')
def template_test():
goods = [{"name": "苹果", "price": 32}, {"name": "橘子", "price": 99}]
return render_template('first.html', **locals())
也可以使用Python字段的语法拿到值
{% for good in goods %}
{{ good["name"] }}
{{ good["price"] }}
{% endfor %}
在代码块中使用loop关键字
这是一个测试
{% for good in goods %}
{{ good.name }}
{{ good.price }}
循环长度{{ loop.length }}
当前索引{{ loop.index0 }}
是否结束{{ loop.last }}
{% endfor %}
Python中的range
可以结合for循环一起使用,在jinja2中也可以直接使用
{% for i in range(20) %}
{{ i }}
{% endfor %}
Jinja2的过滤器
过滤器的本质是一个转换函数
,有时候不仅需要输出程序参数还要对参数进行修改转换才能输出,此时需要用到过滤器,过滤器写在变量后面,中间用|
隔开,|左右没有空格
字符串过滤器
-
default
: 如果程序中没有该参数则可以输出一个默认值,如果程序中存在该参数,但是是None
,""
,0
,False
,可以设置第二个参数,如果设为true
则以上0值输出也跳转到默认值,false
则保留原始空值输出,默认不写的话就是false
{{ name|default('小明', true)}}
这个过滤器也可以用or
关键字实现,和Python语法一致,如果前项为空输出后项,无此参数和有参数但是为空都会走or逻辑
{{ name or '小明'}}
-
replace
: 替换,和Python语法一致,多个过滤器可以连续写
{{ name|default('小明')|replace('小', '大')}}
-
trim
: 去除收尾空格
{{ ' 去除收尾空格 '|trim }}
-
~
:字符串连接,传入name='小明',输出"我和小明出去玩了"
{{ '我和'~name~'出去玩了'}}
可迭代对象过滤器
-
first
和last
:获得列表的第一个和最后一个元素
第一个元素{{ items|first }}
最后一个元素{{ items|last }}
在视图函数中传递一个列表
@app.route('/template_test')
def template_test():
items = [1, 3, 5, 7, 9]
return render_template('first.html', **locals())
输出为"第一个元素1 最后一个元素9"
-
count
: 返回列表的长度
列表长度{{ items|count }}
-
max
,min
求一个列表的最大最小值
{{ ent_scores|max }}
{{ ent_scores|min }}
-
join
:列表转字符串,和Python语法一致,可以指定分隔符,输出为"列表转字符串1,3,5,7,9"
列表转字符串{{ items|join(',') }}
-
sort
:排序,默认不传参是正序排序,传入reverse=true
是降序
列表升序{{ items|sort }}
列表降序{{ items|sort(true) }}
可以指定属性排序,比如需要排序的对象是一个字典集合
items = [{"name": "苹果", "price": 23}, {"name": "西瓜", "price": 33}, {"name": "西红柿", "price": 25}]
为sort过滤器增加attribute
字段指定排序属性,效果如下
根据某个属性排序{{ items|sort(attribute='price', reverse=true) }}
{% for item in items|sort(attribute='price', reverse=true) %}
{{ item.name }}
{% endfor %}
-
reverse
: 可迭代对象反转,字符串和集合都可以
{{ '12345'|reverse }}
{{ [1, 2, 3, 4, 5]|reverse|join(',')}}
数值对象过滤器
-
round
: 保留小数,比如保留2为小数
{{ 2.222|round(2) }}
-
string
:转化为字符串,如果程序传入的参数是数值,在模板中需要进行字符串操作需要转string
{{ 2.22|string + '你好'}}
-
int
:转化为整数,向下取整
{{ 2.222|int }}
-
abs
: 转化为绝对值
{{ -2.222|abs }}
-
-
:直接取负号,取相反数,如下num输入为3,最终输出为0
{{ -num + 3}}
其他jinja2的内置过滤器参考http://www.pythontip.com/blog/post/5455/
过滤器的执行顺序
如果一个{{ }}表达式中既存在过滤器,又存在Python的基本语句,如运算,其他关键字等,过滤器总是和他之前紧贴的那个元素相连,即过滤器优先级最高,因此必要的时候需要使用( )来控制过滤器的范围,比如
{{ (ent_rank * 100)|int ~"%"}}
如果不加入括号,则对ent_rank int取整无效,因为他只对100有效
自定义过滤器
可以自己用Python语言实现一个自定义过滤器使用add_template_filter
进行注册调用,或者使用修饰器template_filter
注册
@app.template_filter('t_func')
def t_func(t):
t2 = time.time()
diff = t2 - t
if diff < 60:
return "刚刚"
elif 60 <= diff < 60 * 60:
return "%d分钟之前" % int(diff / 60)
elif 3600 <= diff < 3600 * 24:
return "%d小时之前" % int(diff / 3600)
else:
return "很久之前"
另一种注册方式
def t_func(t):
t2 = time.time()
diff = t2 - t
if diff < 60:
return "刚刚"
elif 60 <= diff < 60 * 60:
return "%d分钟之前" % int(diff / 60)
elif 3600 <= diff < 3600 * 24:
return "%d小时之前" % int(diff / 3600)
else:
return "很久之前"
app.add_template_filter(t_func, 't_func')
HTML模板
这是一个测试
文章发表于{{ t| t_func }}
最终显示结果为"文章发表于很久之前"
自定义过滤器可以带有一个或者多个参数,函数的第一个参数作为需要渲染转化的参数,后面的参数可以在过滤器括号中指定作为辅助参数,比如
def score_grade(score: int, add: str):
if score >= 90:
return "高风险" + add
elif 90 > score >= 80:
return "中度风险" + add
elif 80 > score >= 60:
return "低风险" + add
elif 60 > score:
return "良好" + add
detail.add_app_template_filter(score_grade, 'score_grade')
以上函数除了要转化的score,还增加一个可变参数,在模板中的使用,过滤器后面使用( )传递参数,如果要传递的参数也是需要渲染的变量,不需要再加{{ }},在外{{ }}内整体渲染
风险等级: 风险等级为{{ ent_score|score_grade('aaa') }}
Jinja2编写宏和导入应用宏
宏类似函数,将一个逻辑块中变动的部分单独抽出来作为参数,其他固定的部分包装在一个程序块中,jinja2中定义宏的关键字是macro
。如下在html中定义一个宏input,有name,type,value三个参数,后面两个参数有默认值,这三个参数对应h5的input的标签,实际上就是用一个宏实现了多种样式的input标签。
这是一个测试
{% macro input(name, type='text', value='') %}
{% endmacro %}
用户名:{{ input('username') }}
密码:{{ input('password', type='password') }}
用户名:{{ input('submit', type='submit', value='提交') }}
定义好宏之后,使用{{ 宏(参数) }}
进行调用,效果如下
一个宏可以被多个h5模板导入使用,所以需要将所有的宏写在一个h5中,导入的方法类似Python的import
。首先新建一个h5命名为macro.html
{% macro input(name, type='text', value='') %}
{% endmacro %}
在另一个模板中导入
这是一个测试
{% import 'macro.html' as macro %}
用户名:{{ macro.input('username') }}
密码:{{ macro.input('password', type='password') }}
用户名:{{ macro.input('submit', type='submit', value='提交') }}
也可以直接从模板中导入宏,使用from import
导入
这是一个测试
{% from 'macro.html' import input %}
用户名:{{ input('username') }}
密码:{{ input('password', type='password') }}
用户名:{{ input('submit', type='submit', value='提交') }}
include的使用
jinja2中将一个模板引入到另一个模板的指定位置使用关键字include
,比如引入公共部分,header.html,footer.html等,先创建公共头部和尾部的h5,include导入的模板必须在templates目录下
header
这是网页头部
footer
另外编写一个模块,使用include导入header.html和footer.html,在主模板中定义的css样式可以被include导入的模板使用
这是一个测试
{% include "header.html" %}
这是正文
{% include "footer.html" %}
效果如下
使用set和with赋值变量
jinja2中set
和with
都可以对变量进行定义和赋值,set
定义的在整个模板范围
中都有效,with
定义的变量有作用范围
,with的作用就是定义这个范围,分别用set和with定义变量如下
这是一个测试
{% set a='123' %}
set的值{{ a }}
{% with b='321' %}
with的值{{ b }}
{% endwith %}
调用定义的值使用{% %}
,如果在with外面调用在with内部定义的值则无效
如果在一个with语句中存在需要需要渲染的变量则直接写入变量名,不需要再加渲染花括号,如果with语句中有多个变量需要定义,中间用逗号隔开,如下
{% with ent_score_diff = ent_scores[-1] - ent_scores[0], ent_max_min_diff = ent_scores|max - ent_scores|min %}
静态文件的加载 url_for
需要先创建static
文件夹,再创建子文件夹css
,js
,images
,同时需要使用url_for
函数。
(1)导入js
也可以使用更通用额方式
(2)导入图片
(3)导入css
模板的集成extends
一个系统的网站每个网页需要有统一的结构,比如每个网页都有标题,内容,底部等几个部分,模板继承的目的就是将相同的部分提取出来
,形成一个base.html。在jinja2中使用extends
关键字。
模板的继承包含基本模板
和子模板
,子模板继承基本模板,基本模板包含了基本骨架,其中有一些空的不完善的块block
需要子模板来填充,不同的block之间用不同的名字区分,因此每个block还需要定义一个名字,比如父模板如下
{% block title %}{% endblock %}
{% block body%}
这是基础身体
{% endblock %}
{% block footer %}
这是尾巴
{% endblock %}
在新模板中继承,继承骨架重写title,body,footer三个block,其中{{ super() }}
方法是使得基础框架中原有的block内的内容保留,默认子模板中的block代码会把基础模块中的对应的block代码覆盖。
{% extends "base.html" %}
{% block title%}这是继承的title{% endblock %}
{% block body %}
{{ super() }}
继承的身体
{% endblock %}
{% block footer %}
继承的尾巴
{% endblock %}
效果如下
Jinja2的空白控制
在最终输出的网页HTML中,Jinja2中的表达式会保留移除后的空行,比如如果模板代码如下
{% if True %}
yay
{% endif %}
最终在浏览器看到的网页源代码是
yay
这是由于Jinja2表达式留下的空行
和jinja2的缩进
导致的,但是多余的空白不影响浏览器的解析,却影响HTML文件体积大小,降低数据传输速度,因此在代码部署前需要去除空格空行,对于缩进没有强制要求,但是为了代码的可读性
,建议在HTML代码中的Jinja2语句中加入缩进。
去除空格空行可以在Jinja2语句的分隔符的%
旁边加入减号-
,紧贴,没有空格,比如
{% for industry_ent in industry_ents[:5] %}
{{ industry_ent }}
{% endfor %}
渲染之后的HTML代码为
在分隔符中加入-号
{% for industry_ent in industry_ents[:5] -%}
{{ industry_ent }}
{%- endfor %}
起始语句-加载右边,带有语句右边的空白去除,结束语句-加载左边,说明语句左边的空白去除,此时再看浏览器的源码如下:
另一种方法是在app中配置jinja的环境参数,就不要在在Jinja2语法的时候时刻注意要写-号了
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
但是宏内的空白不收这两个环境参数控制,还是需要在宏分隔符中手动加入-号,例如
{% macro score_grade_img(filename, width="250", height="220") %}
{% endmacro %}
手动加入-号
{% macro score_grade_img(filename, width="250", height="220") -%}
{%- endmacro %}