文章目录
前言
在Flask中通常使用Jinja2模板引擎来实现复杂的页面渲染,其实Jinja2的设计思想来自于Django的模板引擎,所以这一篇博客主要讲讲Jinja2模板引擎的基本结构和基本使用方法。
正文
1.模板引擎的概述
当我们搭建网站写业务逻辑代码的时候,我们应该是要能保证这份代码在响应式或者非响应式设备上都能使用,再来想想现在常说的前后端分离,后端负责业务逻辑和数据访问,前端负责表现,交互逻辑;为了实现前后端分离,现在把前端的表现逻辑交给视图引擎也就是网页模板。模板实际上就是包含了html+变量的文本文件,当我们用真实值替换模板中的变量之后,生成了对应数据的html片段,这一过程就叫渲染。
来看看前后端分离
了解概念之后来看看Jinja2模板渲染的例子
2.模板引擎的使用
2.1 基础示例
当我们不同路由对应不同视图函数,视图函数中渲染不同的html页面,从而实现在不同路由下得到不同的界面。
首先定义两个html页面,index.html和user.html
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是首页</title>
<h1>这是首页!</h1>
</head>
<body>
</body>
</html>
user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是用户中心</title>
<h1>这是用户中心!</h1>
</head>
<body>
</body>
</html>
app.py
from flask import Flask
from flask import render_template
app=Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<username>')
def user(username):
return render_template('user.html')
if __name__ == '__main__':
app.run(debug=True)
在这个例子,我们引入了一个新的函数render_template(),使用这个函数我们就可以在视图函数中去定义我们要使用哪一个html文件,这个函数之后我们会经常用到。
2.2 模板中参数的传入
除了使用模板引擎渲染html文件,我们还可以把程序中的参数或者变量传递给模板进行渲染。
在上面的index.html,user.html进行一点小改动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是首页</title>
<h1>这是首页!</h1>
</head>
<body>
{{ var1 }}
{{ title}}<br>
{{ var2 }}
{{ author }}
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是用户中心</title>
<h1>这是用户中心!</h1>
<h1>欢迎你:{{ name }}</h1>
</head>
<body>
</body>
</html>
app.py
from flask import Flask
from flask import render_template
app=Flask(__name__)
@app.route('/')
def index():
var1="传进来的标题为:"
var2="传进来的作者为:"
title='Flask专栏'
author='shelgi'
return render_template('index.html',**locals())
#return render_template('index.html',var1=var1,var2=var2,title=title,author=author)
@app.route('/user/<username>')
def user(username):
return render_template('user.html',name=username)
if __name__ == '__main__':
app.run(debug=True)
我们在模板中接受变量的时候就需要把变量放在{{}}中,注释的话用{# #},然后在视图函数中用render_template()渲染的时候就在html后面把需要传递的参数加上就好,当有多个变量需要传参数的时候,可以用locals()方法去代替一个值一个值的传入。
2.3 模板中的控制语句(if,for)
当然我们在模板中也可以使用控制语句if和for,下面来先看看语法
在模板中使用控制语句需要放在{% %}中。
比如{%if 条件%},结尾要加上{% endif %}。
我们先来看看if的使用例子
首先定义一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if randnum %}
<h1>{{ randnum }}</h1>
<h2>产生的随机数有效</h2>
{% else %}
<h1>{{ randnum }}</h1>
<h2>产生的随机数无效</h2>
{% endif %}
</body>
</html>
来看看app.py
from flask import Flask,render_template
import random
app=Flask(__name__)
@app.route('/')
def index():
randnum=random.randint(0,1)
return render_template('Jinja2_if语句.html',**locals())
if __name__ == '__main__':
app.run()
当然除了if else还有if elif,再来看看例子
html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if name==1 %}
<h1>恭喜,抽中一等奖</h1>
{% elif name==2 %}
<h1>恭喜,抽中二等奖</h1>
{% else %}
<h1>恭喜,抽中三等奖</h1>
{% endif %}
{{ name }}
</body>
</html>
app.py
from flask import Flask,render_template
import random
app=Flask(__name__)
@app.route('/')
def index():
randnum=random.randint(1,3)
return render_template('Jinja2_ifelif.html',name=randnum)
if __name__ == '__main__':
app.run()
再来看看for语句
和python使用一样,都是for i in xxx的样子,只是这些语句需要写在{%%}中,并且最后结尾要加上{% endfor %}
直接来看个例子
html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table>
<thead>
<th>商品名称</th>
<th>商品价格</th>
</thead>
<tbody>
{% for good in goods%}
<tr>
<td>{{ good.name }}</td>
<td>{{ good.price }}</td>
</tr>
{% endfor%}
</tbody>
</table>
</body>
</html>
app.py
from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def hello_world():
goods=[{'name':'快餐','price':'50'},
{'name':'海底捞','price':'300'},
{'name':'通宵','price':'20'}]
return render_template('jinja_for循环语句.html',**locals())
if __name__ == '__main__':
app.run()
2.4 Flask的过滤器
过滤器的本质是一个转换函数,很多时候我们不仅要输出变量,还要先将变量的值进行修改,而在模板中不能直接使用python的某些方法,所以这就需要使用过滤器。
常见的过滤器
-
与字符串处理相关的过滤器
- {{name|default(‘None’,true)}} 如果那么为空,就用None替换name
- {{‘hello’|capitalize}} 将字符串首字母大写,显示Hello
- {{‘HELLO’|lowere}} 将字符串字母全部变为小写,显示hello
- {{‘hello’|replace(‘h’,‘x’)}} 将字符串中所有的h替换为x,显示xello
-
与列表处理相关的过滤器
- {{[01,80,42,44,77]|first}} 取得列表中的第一个元素01
- {{01,80,42,44,77]|last}} 取得列表中的最后一个元素77
- {{01,80,42,44,77]|count}} 取得列表中的元素个数5,也可以用length替换count
- {{01,80,42,44,77]|sort}} 将列表中的元素排序,默认是升序排序
- {{01,80,42,44,77]|join(’,’)}} 将列表中的元素合并为字符串 1,80,42,44,77
-
与数值处理相关的过滤器
- {{18.8888|round}} 对数字四舍五入取整,显示19
- {{18.8888|round(2,‘floor’)}} 保留小数点后2位,显示18.88
- {{-2|abs}} 取绝对值,显示2
当然,我们也可以根据自己的具体需要来自定义过滤器
我们可以调用Flask类中的add_template_filter()方法来实现自定义过滤器,其中的参数第一个是自定义过滤器的函数名,第二个是自定义过滤器的名称,来看看具体的例子
html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.line{
display:inline-block;
height: 1px;
width: 100%;
background: #00CCFF;
overflow: hidden;
vertical-align: middle;
}
</style>
</head>
<body>
{% for good in goods %}
<li style="list-style-type: none">{{ good.name }}<span class="{{ loop.index|index_class }}"></span></li>
{% endfor %}
</body>
</html>
app.py
from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def hello_world():
goods=[
{'name':"啊啊的离开"},
{'name':"风格的发货"},
{'name':"得分回复感觉"},
{'name':"地方很多"}]
return render_template('自定义过滤器.html',**locals())
# @app.template_filter("index_class")
def do_index_class(index):
if index%3==0:
return 'line'
else:
return ''
app.add_template_filter(do_index_class,"index_class")
if __name__ == '__main__':
app.run()
首先我们在html中定义了line这个类的样式,然后在app.py中定义了do_index_class()这个自定义的过滤函数,当index是3的倍数时返回line,再用add_template_filter方法,将给函数添加给Jinja2的渲染中,并且把这个过滤器命名为index_class,这样在html文件中,for循环遍历的时候当index是3的倍数时class=line,也就是使用了上面的css样式,出现一条蓝色的横线。
2.5 宏的定义与使用
宏,有声明和定义两部分。
宏的声明:
{# 宏的定义 #}
{% macro input(name,type='text',value='',placeholder="请在这里输入用户名") -%}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" ,placeholder="{{ placeholder }}">
{%- endmacro %}
宏的使用:
<div style="color:#0000FF">
<p>用户名:{{ input('username') }}</p>
<p>密码:{{ input('password',type='password') }}</p>
<p>登陆:{{ input('submit',type='submit',value='登陆') }}</p>
</div>
然后来看看整体的html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>宏的定义和使用</title>
</head>
<body>
{# 宏的定义 #}
{% macro input(name,type='text',value='',placeholder="请在这里输入用户名") -%}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" ,placeholder="{{ placeholder }}">
{%- endmacro %}
{# 宏的使用 #}
<div style="color:#0000FF">
<p>用户名:{{ input('username') }}</p>
<p>密码:{{ input('password',type='password') }}</p>
<p>登陆:{{ input('submit',type='submit',value='登陆') }}</p>
</div>
</body>
</html>
app.py
from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def hello():
return render_template('宏的定义和使用.html')
if __name__ == '__main__':
app.run(debug=True)
当然,我们定义了宏之后,那在不同的模板中都可以导入,这就是宏的导入。
用{% import ‘xxxx.html’ as xxx %}就可以导入编写好的宏,并且命名为xxx从而在自己的模板中使用。当然我们写宏模板的时候也可以引入其他的宏,用include就可以在指定位置引入其他的宏。
2.6 set,with语句的使用以及静态文件的加载
set和with都可以在Jinja2中定义变量并且赋值,但是两者的作用域不同;set是整个模板中都有效,with的作用域在{% with %}{% endwith %}之间。这个使用到后面会举例子,毕竟很简单就不单独举例了。
再来看看静态文件加载,我们可以用html导入静态文件的方式,也可以用url_for()构造地址。我们一般把静态文件放在static文件夹下,所以我们使用link导入url_for(‘static’,filename=‘xxxx’)就可以导入相应的静态文件啦。来看个例子
HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>加载静态文件</title>
<script type="text/javascript" src="static/js/jquery-3.3.1/jquery-3.3.1.js"></script>
<link rel="stylesheet" href="{{ url_for('static',filename='css/car.css') }}">
</head>
<body>
{# 测试jQuery是否加载 #}
<script>
if(jQuery){
alert('jQuery已经加载');
}
else{
alert('JQuery未加载');
}
</script>
<div class="img">
<img src="{{ url_for('static',filename='images/car3.jpg') }}"></img>
</div>
</body>
</html>
这是我的static文件夹结构
app.py
from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def hello():
return render_template('加载静态文件.html')
if __name__ == '__main__':
app.run()
2.7 模板的继承
当我们写网页的时候,通常有标题、内容、底部等等组成,但是每一个网页上都这样写会造成很多冗余,因为基本底部都是一样的,所以我们就可以把这部分重复的单独拿出来写成一个base.html,然后其他模板用到的时候继承这个模板就好,这个就是模板的继承。
模板继承的语法
{% extends "模块名称" %}
在父模块或者说基类模块中,我们除了写好基本骨架,还要为子类模块留下空的或者不完整的块等着继承的子类去填充,来看个例子
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %} -我的网站</title>
<style>
</style>
</head>
<body>
{% block body %}
这是基类中的内容
{% endblock %}
</body>
</html>
在base.html中我们搭建了一个基本html的骨架,但是title和body中留下了块留给子类去填充。
index.html
{% extends "base.html" %}
{% block title %}网站首页{% endblock %}
{% block body %}
{{ super() }}
<h4>这是网站首页的内容!</h4>
{% endblock %}
index.html就继承自base.html,不用再去写基本的html骨架,它关注的是自己是否需要更改父类留的块中的内容。
product.html
{% extends "base.html" %}
{% block title %}产品列表页{% endblock %}
{% block body %}
<h4>这是产品列表页的内容!</h4>
取得网页标题的内容: <h4>{{ self.title() }}</h4>
{% endblock %}
app.py
import flask
from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/product')
def product():
return render_template('product.html')
if __name__ == '__main__':
app.run(debug=True)
总结
Jinja2模板引擎其实主要就是记住那些使用的语法,比如哪些用{{}},哪些用{};哪些需要相应的end代表结束。然后昨天url反转我怕有人还是有点懵,所以今天我在昨天的例子上加一行代码就会更清楚了,来看看。
我今天加上了两行代码,设置了一个url2=url_for(‘index’)还有url3引入一个静态文件,根据我昨天说的,url反转就是传入视图函数,返回它的路由,所以url2应该是/,来看看是不是。
真的就是/,这下知道url_for()函数的作用了吧,当你传入视图函数的时候,它会返回对应的路由,这也叫url反转;当然当你传入的不是视图函数而是static的时候,后面就必须传入其中的某个文件,然后它会把传入的值变成路径格式的字符串。如果传入的既不是视图函数,也不是static静态文件,那就会出错的。