带你读《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,请在模板中遍历出字典的所有属性。

相关文章
|
7天前
|
存储 数据库连接 API
Python环境变量在开发和运行Python应用程序时起着重要的作用
Python环境变量在开发和运行Python应用程序时起着重要的作用
46 15
|
19天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
110 45
|
14天前
|
JSON 安全 API
如何使用Python开发API接口?
在现代软件开发中,API(应用程序编程接口)用于不同软件组件之间的通信和数据交换,实现系统互操作性。Python因其简单易用和强大功能,成为开发API的热门选择。本文详细介绍了Python开发API的基础知识、优势、实现方式(如Flask和Django框架)、实战示例及注意事项,帮助读者掌握高效、安全的API开发技巧。
41 3
如何使用Python开发API接口?
|
6天前
|
JSON API 数据格式
如何使用Python开发1688商品详情API接口?
本文介绍了如何使用Python开发1688商品详情API接口,获取商品的标题、价格、销量和评价等详细信息。主要内容包括注册1688开放平台账号、安装必要Python模块、了解API接口、生成签名、编写Python代码、解析返回数据以及错误处理和日志记录。通过这些步骤,开发者可以轻松地集成1688商品数据到自己的应用中。
22 1
|
12天前
|
开发框架 前端开发 JavaScript
利用Python和Flask构建轻量级Web应用的实战指南
利用Python和Flask构建轻量级Web应用的实战指南
39 2
|
12天前
|
数据采集 存储 JSON
Python爬虫开发中的分析与方案制定
Python爬虫开发中的分析与方案制定
|
20天前
|
数据可视化 开发者 Python
Python GUI开发:Tkinter与PyQt的实战应用与对比分析
【10月更文挑战第26天】本文介绍了Python中两种常用的GUI工具包——Tkinter和PyQt。Tkinter内置于Python标准库,适合初学者快速上手,提供基本的GUI组件和方法。PyQt基于Qt库,功能强大且灵活,适用于创建复杂的GUI应用程序。通过实战示例和对比分析,帮助开发者选择合适的工具包以满足项目需求。
66 7
|
22天前
|
算法 测试技术 开发者
性能优化与代码审查:提升Python开发效率
探讨了Python开发中性能优化和代码审查的重要性,介绍了选择合适数据结构、使用生成器、避免全局变量等性能优化技巧,以及遵守编码规范、使用静态代码分析工具、编写单元测试等代码审查方法,旨在帮助开发者提升开发效率和代码质量。
41 8
|
18天前
|
算法 测试技术 开发者
性能优化与代码审查:提升Python开发效率
性能优化与代码审查:提升Python开发效率
28 1
|
20天前
|
安全 数据库 开发者
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第26天】本文详细介绍了如何在Django框架下进行全栈开发,包括环境安装与配置、创建项目和应用、定义模型类、运行数据库迁移、创建视图和URL映射、编写模板以及启动开发服务器等步骤,并通过示例代码展示了具体实现过程。
31 2