第三章 Jinja2模板

第三章 Jinja2模板

在前面的实例中,视图函数的主要作用是生成请求的响应,这是最简单请求.实际上,视图函数有两个作用:

  • 处理业务逻辑
  • 返回响应内容

在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本.

  • 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
  • 使用真实值替换变量,再返回最终得到的字符串,这个过程称为’渲染’
  • Flask是使用Jinja2这个模板引擎来渲染模板

使用模板的好处

  • 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
  • 而模板则取到视图函数的数据结果进行展示(试图展示方面)
  • 代码结构清晰,耦合度低

1、Jinja2模板介绍

Jinja2:是Python的Web项目中被广泛应用的模板引擎,是由Python实现的模板语言,Jinja2 的作者也是 Flask 的作者。他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。

jinja2之所以被广泛使用是因为它具有以下优点:

  • 1、相对于Template,jinja2更加灵活,它提供了控制结构,表达式和继承等。
  • 2、相对于Mako,jinja2仅有控制结构,不允许在模板中编写太多的业务逻辑。
  • 3、相对于Django模板,jinja2性能更好。
  • 4、Jinja2模板的可读性很棒。

要渲染一个模板,通过 render_template 方法即可。

模板传参

1、在使用 render_template 渲染模版的时候,可以传递关键字参数(命名参数)。以后直接在模版中使用就可以了。

2、 如果你的参数项过多,那么可以将所有的参数放到一个字典中,或者列表中都可以。一般如果想将字典打散成关键字参数可以在参数的前面加 **

from flask import Flask, render_template, redirect, request 
app = Flask(__name__) 
STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'}
STUDENT_LIST = [ 
	{'name': 'Old', 'age': 38, 'gender': '中'}, 
	{'name': 'Boy', 'age': 73, 'gender': '男'}, 
	{'name': 'EDU', 'age': 84, 'gender': '女'} 
]
STUDENT_DICT = { 
	'a': {'name': 'Old', 'age': 38, 'gender': '中'}, 
	'b': {'name': 'Boy', 'age': 73, 'gender': '男'}, 
	'c': {'name': 'EDU', 'age': 84, 'gender': '女'}, 
}

@app.route("/student") 
def detail(): 
	#print(url_for("detail")) 
	return render_template("student.html", **STUDENT) 

@app.route("/detail_list", ) 
def detail_list(): 
	return render_template("student_list.html", stu_list=STUDENT_LIST)
	 
@app.route("/detail_dict") 
def detail_dict(): 
	return render_template("student_dict.html", stu_dict=STUDENT_DICT)

语法

在jinja2中,存在三种语法:

  1. 控制结构 (逻辑代码){% %}
  2. 变量取值 {{ }}
  3. 注释 {# #}

2、表达式

  • 最常用的是变量,由Flask渲染模板时传过来,比如name

  • 也可以是任意一种Python基础类型,比如字符串{{stu_list}};或者数值,列表,元祖,字典,布尔值。

  • 运算。包括算数运算,如{{ 2 + 3 }};比较运算,如{{ 2 > 1 }};逻辑运算,如{{ False and True }}

  • 过滤器|和测试器is

  • 函数调用,如{{ current_time() }};

  • 数组下标操作,如{{ arr[1] }}

  • in操作符,如{{ 1 in [1,2,3] }}

  • 字符串连接符~,作用同Python中的 “+” 一样,如{{ "Hello " ~ name ~ “!” }}

  • None值处理{{name or “”}

3、控制语句

Jinja2的控制语句主要就是条件控制语句if,和循环控制语句for,语法类似于Python if-else:

条件判断语句:

{% if name and name == 'admin' %} 
	<h1>This is admin console</h1> 
{% elif name %} 
	<h1>Welcome {{ name }}!</h1> 
{% else %} 
	<h1>Please login</h1> 
{% endif %} 
for循环语句: 
	{% for stu in stu_list%} 
		{{ stu }} 
	{% endfor %} 

### 

比如:

<!DOCTYPE html> 
<html lang="en"> 
<head>
	<meta charset="UTF-8"> 
	<title>Title</title> 
</head> 
<body> 
{{ stu_list }} 
<table border="1px"> 
	<tr>
		<td>name</td> 
		<td>age</td> 
		<td>gender</td>
	</tr> 
	{% for stu in stu_list %} 
		{% if stu.name != "Old" %} 
			{% if stu.age != 73 %} 
				<tr>
					<td>{{ stu.name }}</td> 
					<td>{{ stu.get("age") }}</td> 
					<td>{{ stu["gender"] }}</td> 
				</tr> 
			{% endif %} 
		{% endif %} 
	{% endfor %} 
</table> 
</body> 
</html> 

或者:

<!DOCTYPE html> 
<html lang="en">
<head>
	<meta charset="UTF-8"> 
	<title>Title</title> 
</head> 
<body> 
{{ stu_dict }} 
<table border="1px"> 
	<tr>
		<td>id</td> 
		<td>name</td> 
		<td>age</td> 
		<td>gender</td> 
	</tr> 
	{% for stu_key,stu_value in stu_dict.items() %} 
		<tr>
			<td>{{ stu_key }}</td> 
			<td>{{ stu_value.get("name") }}</td> 
			<td>{{ stu_value.age }}</td> 
			<td>{{ stu_value.gender }}</td> 
		</tr> 
	{% endfor %} 
</table> 
</body> 
</html>

4、过滤器

什么是过滤器? 实质上就是一个转换函数。变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja2里面的内置

函数和字符串处理函数。

常用的过滤器有:

过滤器名称说明
safe渲染时值不转义
capitialize把值的首字母转换成大写,其他子母转换为小写
lower把值转换成小写形式
upper把值转换成大写形式
title把值中每个单词的首字母都转换成大写
trim把值的首尾空格去掉
striptags渲染之前把值中所有的HTML标签都删掉
join拼接多个值为字符串
replace替换字符串的值
round默认对数字进行四舍五入,也可以用参数进行控制
int把值转换成整型

1、字符串的过滤器

<body> 
{# 当变量未定义时,显示默认字符串,可以缩写为d #} 
<p>{{ name | default('No name') }}</p> 
{# 单词首字母大写 #} 
<p>{{ 'hello world' | capitalize }}</p> 
{# 单词全小写 #} 
<p>{{ 'XML' | lower }}</p> 
{# 去除字符串前后的空白字符 #} 
<p>{{ ' hello ' | trim }}</p> 
{# 字符串反转,返回"olleh" #} 
<p>{{ 'hello' | reverse }}</p> 
{# 格式化输出,返回"Number is 99" #} 
<p>{{ '%s is %d' | format("Number", 99) }}</p> 
{# 关闭HTML自动转义 #} 
<p>{{ '<em>name</em>' | safe }}</p> 
{% autoescape false %} 
{# HTML转义,即使autoescape关了也转义,可以缩写为e #} 
<p>{{ '<em>name</em>' | escape }}</p> 
{% endautoescape %}

2、数值的过滤器

</body> 
{# 四舍五入取整,返回13.0 #} 
<p>{{ 12.98 | round }}</p> 
{# 向下截取到小数点后2位,返回12.88 #} 
<p>{{ 12.8888 | round(2, 'floor') }}</p> 
{# 绝对值,返回12 #} 
<p>{{ -12 | abs }}</p> 

3、列表相关的过滤器

{# 取第一个元素 #} 
<p>{{ [1,2,3] | first }}</p> 
{# 取最后一个元素 #} 
<p>{{ [1,2,3] | last }}</p> 
{# 返回列表长度,可以写为count #} 
<p>{{ [1,2,3,4,5] | length }}</p> 
{# 列表求和 #} 
<p>{{ [1,2,3,4,5] | sum }}</p> 
{# 列表排序,默认为升序 #} 
<p>{{ [3,2,1,5,4] | sort }}</p> 
{# 合并为字符串,返回"1 | 2 | 3 | 4 | 5" #} 
<p>{{ [1,2,3,4,5] | join(' | ') }}</p> 
{# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #} 
<p>{{ ['alex','bob','ada'] | upper }}</p> 

4**、字典相关的过滤器**

{% set users=[{'name':'Tom','gender':'M','age':20}, 
				{'name':'John','gender':'M','age':18},
				{'name':'Mary','gender':'F','age':24}, 
				{'name':'Bob','gender':'M','age':31}, 
				{'name':'Lisa','gender':'F','age':19}] 
%}

{# 按指定字段排序,这里设reverse为true使其按降序排 #} 
<ul> 
{% for user in users | sort(attribute='age', reverse=true) %} 
	<li>{{ user.name }}, {{ user.age }}</li> 
{% endfor %} 
</ul> 

{# 列表分组,每组是一个子列表,组名就是分组项的值 #} 
<ul> 
{% for group in users|groupby('gender') %} 
	<li>{{ group.grouper }}<ul> 
	{% for user in group.list %} 
		<li>{{ user.name }}</li> 
	{% endfor %}</ul></li> 
{% endfor %} 
</ul> 

{# 取字典中的某一项组成列表,再将其连接起来 #} 
<p>{{ users | map(attribute='name') | join(', ') }}</p> 

5、自定义过滤器

# 第一种方式 
def get_even_list(l): 
	return l[::2] 
# 函数的第一个参数是过滤器函数,第二个参数是过滤器名称 
app.jinja_env.filters['even_filter'] =get_even_list 
# 第二种方式 
@app.template_filter() # 过滤器函数 
def is_even(num): 
	if num % 2 == 0: 
		return "even number" 
	else:
		return "odd number" 

模板中:

<p>{{ [1,2,3,4,5] | even_filter }}</p> 

<p>{{ 2 | is_even }}</p>

5、测试器

测试器总是返回一个布尔值,它可以用来测试一个变量或者表达式,使用”is”关键字来进行测试。

{% set name='ab' %} 
{% if name is lower %} 
	<h2>"{{ name }}" are all lower case.</h2> 
{% endif %} 

测试器本质上也是一个函数,它的第一个参数就是待测试的变量,在模板中使用时可以省略去。如果它有第二个参数,模板中就必须传进去。测试器函数返回的必须是一个布尔值,这样才可以用来给if语句作判断。

1、Jinja2中内置的测试器

官网:https://jinja.palletsprojects.com/en/master/templates/#builtin-tests

{# 检查变量是否被定义,也可以用undefined检查是否未被定义 #} 
{% if name is defined %} 
	<p>Name is: {{ name }}</p> 
{% endif %} 
{# 检查是否所有字符都是大写 #} 
{% if name is upper %} 
	<h2>"{{ name }}" are all upper case.</h2> 
{% endif %} 
{# 检查变量是否为空 #} 
{% if name is none %} 
	<h2>Variable is none.</h2> 
{% endif %} 
{# 检查变量是否为字符串,也可以用number检查是否为数值 #} 
{% if name is string %} 
	<h2>{{ name }} is a string.</h2> 
{% endif %} 
{# 检查数值是否是偶数,也可以用odd检查是否为奇数 #} 
{% if 2 is even %} 
	<h2>Variable is an even number.</h2> 
{% endif %} 
{# 检查变量是否可被迭代循环,也可以用sequence检查是否是序列 #} 
{% if [1,2,3] is iterable %} 
	<h2>Variable is iterable.</h2> 
{% endif %} 
{# 检查变量是否是字典 #} 
{% if {'name':'test'} is mapping %} 
	<h2>Variable is dict.</h2>
{% endif %} 

2、自定义测试器

# 自定义测试器 
# 第一种方式 
import re 
def test_tel(tel_num): 
	tel_re = r'\d{11}' 
	return re.match(tel_re,tel_num) 
app.jinja_env.tests['is_tel'] = test_tel 
# 第二种方式 
@app.template_test('start_with') 
def start_with(str, suffix): 
	return str.lower().startswith(suffix.lower()) 

模板中:

{% set tel = '18910171111' %} 
{% if tel is is_tel %} 
	<h2>{{ tel }} is mobile phone</h2> 
{% endif %} 
{% set name = 'Hello world' %} 
{% if name is start_with 'hello' %} 
	<h2>"{{ name }}" start_with "hello"</h2> 
{% endif %} 

6、块和继承

一般我们的网站虽然页面多,但是很多部分是重用的,比如页首,页脚,导航栏之类的。对于每个页面,都要写这些代码,很麻烦。

Flask的Jinja2模板支持模板继承功能,省去了这些重复代码。

父模板:

<body> 
你好,template1 
{% block template1 %} 
{% endblock %} 

你好,template2 
{% block template2 %} 
{% endblock %} 

你好,template
{% block template %} 
{% endblock %} 
</body> 

子模版:

{% extends "he.html" %} 

{% block template %} 
	{{ super() }} 
	<h1>yuan</h1> 
{% endblock %} 

{% block template1 %} 
	{{ super() }} 
	<h1>alex</h1> 
{% endblock %} 

include标签

这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位置。 include 标签,如果想要使用父模版中的变量,直接用就可以了。 include 的路径,也是跟 import 一样,直接从 templates 根目录下去找,不要以相对路径去找。

<!DOCTYPE html> 
<html lang="en"> 
<head>
	<meta charset="UTF-8"> 
	<title>SXT</title> 
</head> 
<body>
	<!--通过include 引入头部log信息--> 
	{% include "common/head.html" %} 
	<div>
		这是首页内容 
		{{ major }} 
	</div> 
	<hr> 
	<!--通过include 引入底部版权信息--> 
	{% include "common/footer.html" %} 
</body> 
</html>

7、模板中使用url_for

模版中的 url_for 跟我们后台视图函数中的 url_for 使用起来基本是一模一样的。也是传递视图函数的名字,也可以传递参数。使用的时候,需要在 url_for 左右两边加上一个 {{ url_for('func') }}

1、动态超链接

html页面使用如: 
<a href="{{ url_for('login',p1='abc',p2='ddd',name='momo') }}">登录</a> 
本质上就是动态路由 
点击变为: 
http://127.0.0.1:5000/accounts/login/momo/?p1=abc&p2=ddd 

对应的视图函数:

@app.route('/accounts/login/<name>/') 
def login(name): 
	print(name) 
	return render_template('login.html') 

或者也可以不用动态路由:

html页面使用如: 
<a href="{{ url_for('login',p1='abc',p2='ddd') }}">登录3</a> 
点击变为: 
http://127.0.0.1:5000/accounts/login/?p1=abc&p2=ddd

2、加载静态文件

静态文件:css文件 js文件 图片文件等文件

加载静态文件使用的是 url_for 函数。然后第一个参数需要为 static ,第二个参数需要为一个关键字参数

filename='路径'

语法:
{{ url_for(“static”,fifilename=‘xxx’) }}

<link href="{{ url_for('static',filename='css/main.css') }}" rel="stylesheet"> 
<script src="{{ url_for('static',filename='js/main.js') }}"></script> 
<img src="{{ url_for('static',filename='img/main.jpg') }}"/>
  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值