带你读《Python Flask Web开发入门与项目实战》之三:Jinja 2模板引擎

简介: 本书从Flask框架的基础知识讲起,逐步深入到使用Flask进行Web应用开发实战。内容通俗易懂,案例丰富,实用性强,特别适合Python Web开发的入门读者和进阶读者学习,也适合PHP程序员和Java程序员等其他Web开发爱好者阅读。另外,本书可以作为相关培训机构的教材用书。

点击查看第一章
点击查看第二章

第3章Jinja 2模板引擎

在Flask中通常使用Jinja 2模板引擎来实现复杂的页面渲染。Jinja 2被认为是灵活、快速安全的模板引擎技术,被广泛使用。Jinja 2的设计思想来源于Django模板引擎, 它功能强大、速度 快,并且提供了可选的沙箱模板执行环境安全保证机制,具有沙箱中执行、强大的HTML转义系统、模板继承等诸多优点。本章主要介绍Jinja 2模板引擎的基本结构、基本使用方法。
本章主要涉及的知识点有:

  • 如何使用Flask渲染模板;
  • 在模板中传递一个或多个参数;
  • if语句在模板中的使用;
  • for语句在模板中的使用。

3.1模板引擎概述及简单使用

随着不同终端(个人PC、平板电脑,手机、移动穿戴设备等)的兴起,开发人员在越来越多地思考:如何写一份功能代码(业务逻辑代码),这份业务逻辑代码能够在响应式或非响应式设备上都能使用。为了提升开发效率,开发人员开始高度重视前后端的分离,后端负责业务逻辑/数据访问,前端负责表现、交互逻辑,同一份业务逻辑代码可应用于多个不同终端的视图渲染。后端实际上实现的功能一般叫做业务逻辑,前端完成的功能一般叫做表现逻辑。如果把业务逻辑和表现逻辑混在一起,势必造成系统耦合度高、代码维护困难的现象,因此分离业务逻辑和表现逻辑,把变现逻辑交给视图引擎,即网页模板,很有必要。
模板实质上是一个静态的包含HTML语法的全部或片段的文本文件,也可包含由变量表示的动态部分。使用真实值替换网页模板中的变量,生成对应数据的HTML片段,这一过程称为渲染。Flask提供了Jinja 2模板引擎来渲染模板,下面逐步介绍其模板渲染机制。
在PyCharm中新建一名称为3-1的工程,在工程中templates的文件夹下新建index.html文件,代码如下:

image.png

在工程中templates的文件夹下新建user.html文件,代码如下:

image.png


  
app.py文件的代码如下:

image.png


 
01行表示导入Flask模块;02行表示导入render_template模块;03行表示Flask初始化;04行定义路由;05行定义视图函数;06行使用render_template()方法渲染模板;07行定义路由;08行定义视图函数;09行使用render_template()方法渲染模板;10行表示当模块被直接运行时,代码将被运行,当模块是被导入时,代码不被执行;11行表示开启调试模式。
运行程序,得到如3.1图所示结果。

image.png

Flask通过render_template()函数来实现模板的渲染。要使用Jinja 2模板引擎,需要使用from flask import render_template命令导入render_template函数。在视图函数的return方法中,render_template()函数的首个参数声明使用哪一个模板文件。

image.png

3.2向模板中传递参数

Flask提供Jinja 2模板引擎来渲染模板的同时,还可以将程序中的参数或变量值传递给指定的模板进行渲染。
在PyCharm中新建一名称为3-2的工程,在工程中的templates文件夹下新建index.html文件,代码如下:

image.png

在工程中templates文件夹下新建user.html文件,代码如下:

image.png


  
app.py文件的代码如下:

image.png

01行表示导入Flask模块;02行表示导入render_template模块;03行表示Flask初始化;04行表示定义路由;05行表示定义视图函数;06行渲染模板;07行定义路由;08行表示定义视图函数;09行表示渲染模板并向模板传递参数;10行表示当模块被直接运行时,代码将被运行,当模块被导入时,代码不被执行。
运行程序,得到如图3.2所示结果。

image.png

render_template()函数第一个参数是指定模板文件的名称,比如这里的index.html和user.html。render_template( )函数的第二个参数为可选项,可以为空。比如index()视图函数中的render_template('index.html'),这里第二个参数为空。第二个参数不为空的话,一般用于向模板中传递变量。这里传递变量,一般是以键值对方式进行的。

   01 @app.route('/')
  02 def index():
  03 title = 'python的键值对'
  04 author='tom_jack'
  05 return render_template('index.html', var1=title, var2=author)

用上述代码替换index()视图函数代码,在index.html的body块儿区域增加下面的代码:

   01 <body>
  02 {{ var1 }}<br> {#br表示网页中的回车#}
  03 {{ var2 }}
  04 </body>

再次运行程序,得到如图3.3所示结果。

image.png

模板中接收变量值,需要把变量放在{{ }},比如{{ var1 }}等。模板中如果要写注释,格式为{# #},比如这里的{#br表示网页中的回车#}。
如果视图函数中有多个变量值都需要传递给模板,可以使用**locals()方法,例如:

   01 def index():    #定义index函数
  02   # return render_template('index.html')
  03  title = 'python的键值对'    #定义键值
  04  author = 'tom_jack'    #定义键值
  05  return render_template('index.html', **locals())    #渲染模板并传值

实际上是将return render_template('index.html', var1=title, var2=author)这行代码替换为return render_template('index.html', **locals())。将模板文件index.html中的{{ var1 }}
{{ var2 }}替换为{{ title }}
{{ author }}即可。
注意:在render_template()函数中,如果要给模板传递全部的本地变量,可以使用**locals()方法,此时,在模块中可以直接使用{{title}}和{{author}}来直接使用变量。

3.3模板中的控制语句之if语句

在Jinja 2模板引擎中也可以使用if和for循环控制语句,控制模板渲染的方向。模板引擎中,if和for语句中应该放到{% %}中。
本节我们首先看看模板中的if语句如何使用。在前端的Jinja 2语法中,if可以进行判断:是否存在参数,存在的参数是否满足条件,其基本语法如下:

   01 {% if condition %}    <!-- condition指的是条件-->
  02 {% else %}    <!-- 条件不满足时-->
  03 {% endif %}    <!-- 结束if语句-->

在PyCharm中新建一个名称为3-3的工程。在工程中的templates文件夹下新建index.html文件,代码如下:

image.png


 
app.py对应的代码如下:

image.png

import random表示导入Python的随机库,rand1=random.randint(0,1)表示产生0~1范围内的整型数。在模板中进行判断,如果产生的数据为1,视为有效,如果产生的数据为0,视为无效数据。运行本项目代码,结果如图3.4所示。可以多次刷新,看看输出结果有何不同。

image.png

在PyCharm中新建一名称为3-4的工程。在工程中的templates文件夹下新建index.html文件,代码如下:

image.png


 
app.py文件内容如下:

image.png

运行项目代码,运行结果如图3.5所示。
在模板中,尽量少使用多层嵌套的 if…else…语句,往往会因为缩进出现这样或那样的问题。尽量多用if…elif…else…的结构(即多个elif),这一系列条件判断会从上到下依次判断,如果某个判断为True,执行完对应的代码块,后面的条件判断就会直接忽略,不再执行。

image.png

image.png

3.4模板中的控制语句之for语句

首先,我们回顾一下Python中的for循环语句。for循环语句是Python中的一个循环控制语句,任何有序的序列对象内的元素都可以遍历,比如字符串、列表、元组等可迭代对像。for循环的语法格式如下:  

for 目标 in 对象:
      循环体

 
比如,使用for循环一个字符串,输出字符串中每位字符的操作方法如下:

   01  #encoding:utf-8    #指定编码
  02  str='www.google.com'    #定义字符串
  03  for str1 in str:     #for循环进行遍历
  04  print(str1)     #打印输出

运行程序,屏幕上可以输出www.google.com中每一个字符。那么模板中的for循环又该如何使用呢?模板中的for语句定义如下:

   01 {% for 目标 in对象 %}
  02 <p>目标</p>
  03 {% endfor %}

Jinja 2中for循环内置常量:

  • loop.index:当前迭代的索引(从1开始);
  • loop.index0:当前迭代的索引(从0开始);
  • loop.first:是否是第一次迭代,返回True或False;
  • loop.last是否是最后一次迭代,返回True或False;
  • loop.length:返回序列的长度。

image.png

下面以视图函数定义一个字典goods,在模板中使用for循环渲染输出。在PyCharm中新建一名称为3-5的工程。在工程中的templates文件夹下新建shop.html文件,代码如下:

image.png


 
上面的代码实现了一个静态页面,代码中定义了一个表格,表格分为5行2列进行显示,其中,第1行用来显示表格“商品名称”及“商品价格”等内容。
app.py文件的内容如下:

image.png

在hello_world视图函数中定义一列表goods,其属性主要有name和price,用for语句将其遍历出来。运行程序,运行结果如图3.6所示。

image.png

3.5Flask的过滤器

过滤器本质上是一个转换函数,有时候我们不仅需要输出变量的值,还需要把某个变量的值修改后再显示出来,而在模板中不能直接调用Python中的某些方法,这么这就用到了过滤器。

3.5.1常见过滤器

1.与字符串操作相关的过滤器

<p>{{name|default('None',true)}</p>

其中,name为变量名,如果name为空,则用None这个值去替换name。
-

<p>{{'hello'|capitalize}}</p>

将字符串hello转化成Hello,实现首字母大写的目的。
-

<p>{{'HELLO'|lowere}}</p>

将字符HELLO全部转为小写。
-

<p>{{'hello'|replace('h','x')}}</p>

将hello中的字母h替换成x。

2.对列表进行操作相关的过滤器

  • {{[01,80,42,44,77]|first}}


    取得列表中的首个元素01。
  • {{[01,80,42,44,77]|last}}


    取得列表中的最后一个元素77。
  • {{[01,80,42,44,77]|count}}


    取得列表中的元素个素,统计个数为5,count也可以使用length替换。
  • {{[01,80,42,44,77]|sort}}


    列表中的元素重新排序,默认按照升序进行排序。
  • {{[01,80,42,44,77]|join(','}}


    将列表中的元素合并为字符串,返回1,80,42,44,77。

3.对数值进行操作相关的过滤器

  • {{18.8888|round}}


    四舍五入取得整数,返回19.0。
  • {{18.8888|round(2,’floor’)}}


    保留小数点后2位,返回结果为18.88。
  • {{-2|abs}}

求绝对值运算,返回结果为2。
下面以列表中的每间隔2行换颜色为例,详细说明模板中过滤器的使用方法。
在PyCharm中新建一名称为3-6的工程。在工程中的templates文件夹下新建index.html和app.py文件,index.html文件代码如下:

image.png

image.png


 
app.py文件的代码如下:

image.png

3.5.2自定义过滤器

内置的过滤器不满足需求怎么办?过滤器的实质就是一个转换函数,我们其实完全可以写出属于自己的自定义过滤器。
通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称。
有一个商品列表页,要求每3行输出一条分割线。在PyCharm中新建一名称为3-7的工程。在工程中的templates文件夹下新建index.html和app.py文件,index.html文件代码如下:

image.png

image.png 

上面的代码对传递过来的列表进行遍历,每3行输出一条分割线,分割线的样式由07~13行所对应的代码定义。
app.py文件的代码如下:

image.png

02~05行导入相应模块,有非UTF-8编码范围内的字符时就要使用sys.setdefaultencoding()方法予以修正。04行表示Flask初始化;05行表示定义路由;06行定义视图函数;08~12行定义列表goods;13行表示渲染模板,并向模板传递参数;14行定义函数;15、16行表示每间隔3行返回一个line;19行表示使用自定义过滤器添加CSS。

3.6 宏的定义及使用

Jinja 2中的宏功能有些类似于传统程序语言中的函数,它跟Python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来作为一个变量。

3.6.1 宏的定义

宏(Macro),有声明和调用两个部分。让我们先声明一个宏:

01 <!--定义宏-->
  02 {% macro input(name, type='text', value= ' ') -%}
  03 ????<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
  04 {%- endmacro %}

上面的代码定义了一个宏,定义宏要加macro,宏定义结束要加endmacro标志。宏的名称就是input,它有3个参数,分别是name、type和value,后两个参数有默认值。我们可以使用表达式来调用这个宏:

01 <!--调用宏-->
  02 {{ input('username')}}
  03 {{ input('password',type='password')}}

  
在Pycharm中新建一名为3-8的工程。在工程中templates的文件夹下新建index.html文件,index.html代码如下:

image.png

image.png

执行网页后,生成对应的代码如下:

image.png


  
上面的代码定义了一个宏,这个宏有3个参数,分别是name、type和value,然后用这个宏定义了两个文本输入框,定义了一个提交按钮。
app.py文件中需要引入模板文件,如下:

image.png


  
01行导入Flask和render_template模块;02行表示Flask初始化;03行表示定义路由;04行表示定义视图函数;05行渲染模板;06行表示当模块被直接运行时,代码将被运行,当模块是被导入时,代码不被执行;07行表示开启调试模式。
运行上面的程序,结果如图3.7所示。

image.png

image.png

3.6.2 宏的导入

一个宏可以被不同的模板使用,所以我们建议将其声明在一个单独的模板文件中。需要使用时导入即可,而导入的方法类似于Python中的import。我们把3.6.1节中的宏定义部分单独放在一个文件中。
在Pycharm中新建一名称为3-9的工程。在工程中的templates文件夹下新建index.html文件和form.html文件。index.html文件代码如下:


   image.png

form.html文件代码如下:

image.png


  
上面的代码定义了一个宏,定义宏要加macro,宏定义结束要加endmacro标志。宏的名称就是input,它有3个参数,分别是name、type和value,后两个参数有默认值。
app.py文件的代码如下:

image.png


  
01行表示导入Flask及render_template模块;02行表示Flask初始化;03行表示定义路由;04行定义视图函数;05行渲染模板。

image.png

3.6.3 include的使用

宏文件中引用其他宏,可以使用include语句。include语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码复制到另外一个模板的指定位置。下面通过一个实例来说明。
在PyCharm中新建一个名为3-10的工程。在工程中的templates文件夹下新建index.html文件、header.html文件及footer.html文件。index.html文件代码如下:

image.png


  
header.html文件内容如下:

image.png

image.png


  
footer文件内容如下:

image.png


  
app.py文件内容如下:

image.png


  
01行表示导入Flask及render_template模块;02行表示Flask初始化;03行定义路由;04行定义视图函数;05行表示渲染模板。
include把一个模板的代码复制到另外一个模板的指定位置,这里的{% include ''header.html'' %}和{% include ''footer.html'' %}把头文件和尾部文件引入到index.html文件中。

image.png

3.7 set和with语句的使用

set与with语句都可以在Jinja 2中定义变量并赋予值。set定义的变量在整个模板范围内都有效,with关键字在定义变量并赋值的同时,限制了with定义变量的作用范围。
首先介绍一下set关键字的使用方法:
(1)给变量赋值:

{% set telephone ='1388888888' %}

(2)给列表或数组赋值:

{% set nav = [('index.html', 'index'), ('product.html', 'product)] %}

可以在模板中使用{{ telephone }}和{{ nav }}来引用这些定义的变量。
接下来介绍with关键字的使用方法,例如:

{% with pass = 60 %}
   {{ pass }}
   {% endwith %}

with定义的变量的作用范围在{% with %}和{% endwith %}代码块内,在模板的其他地方,引用此变量值无效。
在PyCharm中新建一名为3-11的工程。在工程中templates的文件夹下新建index.html文件,index.html文件代码如下:

image.png


  
app.py文件内容如下:

image.png

image.png

运行上面的工程,结果如图3.8所示。

image.png

3.8 静态文件的加载

静态文件的加载一般需要先新建文件夹static,在文件夹下再新建css、js和images文件夹,在这些文件夹中存放css、js、images,同时要需要使用'url_for'函数。
在PyCharm中新建一个名为3-12的工程。找到static文件夹,在此文件夹下再新建css、js和images这3个文件夹,目录结构如图3.9所示。

image.png

image.png

在templates目录下新建一个名为index.html的文件, 在app.py的视图函数中使用return render_template('index.html')方法来渲染模板。下面分别给出加载JS、图片和CSS的方法。
(1)加载JS文件
在静态文件index.html中,在之前引入jquery-3.3.1.js文件,具体代码如下:

<script src="{{ url_for('static', filename='js/jquery-3.3.1/jquery-3.3.1.js') }}">
  </script>

可以使用下面代码测试jquery-3.3.1.js文件是否加载成功。

<script>
      if(jQuery) {
          alert('jQuery已加载!');
      }
      else {
          alert('jQuery未加载!');
      }
  </script>

通过测试,可以发现通过上述方法可以正常加载js文件。这里使用到了url_for()函数来实现。事实上,还可以通过下面代码实现:

<script type="text/javascript" src="static/js/jquery-3.3.1/jquery-3.3.1.js">
  </script>

不过,一般建议使用url_for()函数形式。
(2)加载图片文件。
加载图片,可以使用下述代码实现:

<img src="{{ url_for('static', filename='images/car.jpg') }}"></img>

(3)加载CSS文件。
加载外部CSS文件,可以使用下述代码实现:

<link rel="stylesheet" href="{{ url_for('static',filename='css/car.css') }}">

car.css的代码如下:

.img{
  BORDER-RIGHT: #000 1px solid; BORDER-TOP: #000 1px solid; MARGIN: 10px 0px; BORDER-LEFT: #000 1px solid; BORDER-BOTTOM: #000 1px solid
          }

在index.html文件中添加如下代码:

image.png

image.png

app.py文件的代码如下:

image.png

运行上述代码,效果如图3.10所示。

image.png

3.9 模板的继承

一个系统网站往往需要统一的结构,这样看起来比较“整洁”。比如,一个页面中都有标题、内容显示、底部等几个部分。如果在每一个网页中都进行这几部分的编写,那么整个网站将会有很多冗余部分,而且编写的网页程序也不美观。这时可以采用模板继承,即将相同的部分提取出来,形成一个base.html,具有这些相同部分的网页通过继承base.html来得到对应的模块。
1.模板的继承语法
模板的继承语法如下:

{% extends “模板名称” %}

2.块的概念
模板继承包含基本模板和子模板。其中,基本模板里包含了网站里基本元素的基本骨架,但是里面有一些空的或不完善的块(block)需要用子模板来填充。
在父模板中:  

  …
  {% block block的名称 %}
  {% endblock %}
  … 

 
在子模板中:

…
  {% block block的名称 %}
  子模板中代码
  {% endblock %}
  …

  
在PyCharm中新建一个名为3-13的工程。在templates目录中创建index.html、base.html和product.html 3个静态文件。base.html文件作为基类,index.html和product.html文件作为子类,子类去继承基类的基本内容。
base.html文件内容如下:

image.png


  
index.html文件内容如下:

image.png


  
product.html文件的内容:

image.png


  
app.py文件内容如下:

image.png


  
默认情况下,子模板如果实现了父模板定义的block,那么子模板block中的代码就会覆盖父模板中的代码。如果想要在子模板中仍然保持父摸板中的代码,那么可以使用{{super()}}来实现,如index.html中{% block body %}{% endblock %}代码块中使用了{{ super() }}方法,运行结果如图3.11所示:

image.png

如果想要在一个block中调用其他block中的代码,可以通过{{self.其他block名称()}}实现。比如product.html文件中的

{{ self.title() }}

方法。运行此代码,结果如图3.12所示。

image.png

image.png

3.10 温 故 知 新

1.学完本章内容后,读者需要回答:
(1)什么是模板?
(2)模板中如何写注释?
(3)模板中如何使用if语句?
(4)模板中如何使用for语句?

2.在下一章中将会学习:
(1)路由函数的使用。
(2)装饰器的基本使用。
(3)蓝图的定义和基本使用。

3.11 习 题

通过下面的习题来检验本章的学习情况,习题答案请参考本书配套资源。
1.使用for语句,新建一个工程,打印出九九乘法表。
2.在视图函数中定义一个字典books,请在模板中遍历出字典的所有属性。

相关文章
|
1月前
|
安全 测试技术 网络安全
如何在Python Web开发中进行安全测试?
如何在Python Web开发中进行安全测试?
|
1月前
|
安全 关系型数据库 测试技术
学习Python Web开发的安全测试需要具备哪些知识?
学习Python Web开发的安全测试需要具备哪些知识?
34 4
|
1月前
|
存储 监控 安全
如何在Python Web开发中确保应用的安全性?
如何在Python Web开发中确保应用的安全性?
|
3天前
|
IDE 测试技术 开发工具
10个必备Python调试技巧:从pdb到单元测试的开发效率提升指南
在Python开发中,调试是提升效率的关键技能。本文总结了10个实用的调试方法,涵盖内置调试器pdb、breakpoint()函数、断言机制、logging模块、列表推导式优化、IPython调试、警告机制、IDE调试工具、inspect模块和单元测试框架的应用。通过这些技巧,开发者可以更高效地定位和解决问题,提高代码质量。
46 8
10个必备Python调试技巧:从pdb到单元测试的开发效率提升指南
|
25天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
46 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
17天前
|
存储 API 数据库
使用Python开发获取商品销量详情API接口
本文介绍了使用Python开发获取商品销量详情的API接口方法,涵盖API接口概述、技术选型(Flask与FastAPI)、环境准备、API接口创建及调用淘宝开放平台API等内容。通过示例代码,详细说明了如何构建和调用API,以及开发过程中需要注意的事项,如数据库连接、API权限、错误处理、安全性和性能优化等。
69 5
|
21天前
|
前端开发 安全 JavaScript
2025年,Web3开发学习路线全指南
本文提供了一条针对Dapp应用开发的学习路线,涵盖了Web3领域的重要技术栈,如区块链基础、以太坊技术、Solidity编程、智能合约开发及安全、web3.js和ethers.js库的使用、Truffle框架等。文章首先分析了国内区块链企业的技术需求,随后详细介绍了每个技术点的学习资源和方法,旨在帮助初学者系统地掌握Dapp开发所需的知识和技能。
2025年,Web3开发学习路线全指南
|
28天前
|
存储 前端开发 JavaScript
如何在项目中高效地进行 Web 组件化开发
高效地进行 Web 组件化开发需要从多个方面入手,通过明确目标、合理规划、规范开发、加强测试等一系列措施,实现组件的高效管理和利用,从而提高项目的整体开发效率和质量,为用户提供更好的体验。
31 7
|
1月前
|
机器学习/深度学习 人工智能 关系型数据库
Python开发
Python开发
40 7
|
1月前
|
开发框架 搜索推荐 数据可视化
Django框架适合开发哪种类型的Web应用程序?
Django 框架凭借其强大的功能、稳定性和可扩展性,几乎可以适应各种类型的 Web 应用程序开发需求。无论是简单的网站还是复杂的企业级系统,Django 都能提供可靠的支持,帮助开发者快速构建高质量的应用。同时,其活跃的社区和丰富的资源也为开发者在项目实施过程中提供了有力的保障。