Python的Flask框架中的Jinja2模板引擎学习教程


Posted in Python onJune 30, 2016

Flask的模板功能是基于Jinja2模板引擎来实现的。模板文件存放在当前目前下的子目录templates(一定要使用这个名字)下。
main.py 代码如下:

from flask import Flask, render_template
 
app = Flask(__name__)
 
@app.route('/hello')
@app.route('/hello/<name>')
def hello(name=None):
  return render_template('hello.html', name=name)
 
 
if __name__ == '__main__':
  app.run(debug=True)

hello.html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hello Sample</title>
</head>
<body>
{% if name %}
  <h1>Hello {{ name }} !</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}
 
</body>
</html>

模板的表达式都是包含在分隔符”{{ }}”内的;控制语句都是包含在分隔符”{% %}”内的;另外,模板也支持注释,都是包含在分隔符”{# #}”内,支持块注释。
表达式

表达式一般有这么几种:

  • 最常用的是变量,由Flask渲染模板时传过来,比如上例中的”name”
  • 也可以是任意一种Python基础类型,比如字符串{{ “Hello” }},用引号括起;或者数值,列表,元祖,字典,布尔值。直接显示基础类型没啥意义,一般配合其他表达式一起用
  • 运算。包括算数运算,如{{ 2 + 3 }};比较运算,如{{ 2 > 1 }};逻辑运算,如{{ False and True }}
  • 过滤器“|”和测试器“is”。这个在后面会介绍
  • 函数调用,如{{ current_time() }};数组下标操作,如{{ arr[1] }}
  • “in”操作符,如{{ 1 in [1,2,3] }}
  • 字符串连接符”~”,作用同Python中的”+”一样,如{{ “Hello ” ~ name ~ “!” }}
  • “if”关键字,如{{ ‘Hi, %s' % name if name }}。这里的”if”不是条件控制语句。

控制语句

Jinja2的控制语句主要就是条件控制语句if,和循环控制语句for,语法类似于Python。我们可以改动下上节的模板代码:

{% if name and name == 'admin' %}
  <h1>This is admin console</h1>
{% elif name %}
  <h1>Welcome {{ name }}!</h1>
{% else %}
  <h1>Please login</h1>
{% endif %}

上面是一个条件控制语句的例子,注意if控制语句要用”{% endif %}”来结束。模板中无法像代码中一样靠缩进来判断代码块的结束。再来看个循环的例子,我们先改下Python代码中的”hello”函数,让其传两个列表进模板。

def hello(name=None):
  return render_template('hello.html', name=name, digits=[1,2,3,4,5],
              users=[{'name':'John'},
                 {'name':'Tom', 'hidden':True},
                 {'name':'Lisa'}
                 {'name':'Bob'}])

模板如下:

{% if name and name == 'admin' %}
  <h1>Helle admin</h1>
{% elif name %}
  <h1>"Hello" ~ {{ name }} ~ "!"</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}
 
  {% for digit in digits %}
  {{ digit }}
  {% endfor %}

同if语句一样,for控制语句要用”{% endfor %}”来结束。页面上,每个元素之间会有空格,如果你不希望有空格,就要在”for”语句的最后,和”endfor”语句的最前面各加上一个”-“号。如:

{% for digit in digits -%}
  {{ digit }}
 {%- endfor %}

你可以看到数字”12345″被一起显示出来了。我们再来看个复杂的循环例子:

<dl>
  {% for user in users if not user.hidden %}
  {% if loop.first %}
  <div>User List:</div>
  {% endif %}
  <div class="{{ loop.cycle('odd', 'even') }}">
  <dt>User No. {{ loop.index }}</dt>
  <dd>{{ user.name }}</dd>
  </div>
  {% if loop.last %}
  <dir>Total Users: {{ loop.length }}</dir>
  {% endif %}
  {% else %}
  <li>No users found</li>
  {% endfor %}
</dl>

这里有三个知识点。首先for循环支持else语句,当待遍历的列表”users”为空或者为None时,就进入else语句。
其次,在for语句后使用if关键字,可以对循环中的项作过滤。本例中,所有hidden属性为True的user都会被过滤掉。
另外,for循环中可以访问Jinja2的循环内置变量。本例中,我们会在第一项前显示标题,最后一项后显示总数,每一项显示序号。另外,奇偶项的HTML div元素会有不同的class。如果我们加入下面的CSS style,就能看到斑马线。

<style type="text/css">
  .odd {
    background-color: #BDF;
  }
</style>

Jinja2的循环内置变量主要有以下几个:

1 2 3 4 5 6 7 8 9 10 11 12 from flask import Flask, render_template   app = Flask(__name__)   @app.route('/hello') @app.route('/hello/') def hello(name=None):     return render_template('hello.html', name=name)     if __name__ == '__main__':     app.run(debug=True)

另外,如果你启用了”jinja2.ext.loopcontrols”扩展的话,你还可以在循环中使用”{% break %}”和”{% continue %}”来控制循环执行。
其它常用语句:

忽略模板语法

有时候,我们在页面上就是要显示”{{ }}”这样的符号怎么办?Jinja2提供了”raw”语句来忽略所有模板语法。

{% raw %}
  <ul>
  {% for item in items %}
    <li>{{ item }}</li>
  {% endfor %}
  </ul>
{% endraw %}

自动转义

我们将本文一开始的Flask代码”hello()”方法改动下:

@app.route('/hello')
@app.route('/hello/<name>')
def hello(name=None):
  if name is None:
    name = '<em>World</em>'
  return render_template('hello.html', name=name)

此时,访问”http://localhost:5000/hello”,页面上会显示”Welcome <em>World</em>!”,也就是这个HTML标签”<em>”被自动转义了。Flask会对”.html”, “.htm”, “.xml”, “.xhtml”这四种类型的模板文件开启HTML格式自动转义。这样也可以防止HTML语法注入。如果我们不想被转义怎么办?

{% autoescape false %}
 <h1>Hello {{ name }}!</h1>
{% endautoescape %}

将”autoescape”开关设为”false”即可,反之,设为”true”即开启自动转义。使用”autoescape”开关前要启用”jinja2.ext.autoescape”扩展,在Flask框架中,这个扩展默认已启用。
赋值

使用”set”关键字给变量赋值:

{% set items = [[1,2],[3,4,5]] %}

with语句

类似于Python中的”with”关键字,它可以限制with语句块内对象的作用域:

{% with foo = 1 %}
  {% set bar = 2 %}
  {{ foo + bar }}
{% endwith %}
{# foo and bar are not visible here #}

使用”with”关键字前要启用”jinja2.ext.with_”扩展,在Flask框架中,这个扩展默认已启用。
执行表达式

{% with arr = ['Sunny'] %}
 {{ arr.append('Rainy') }}
 {{ arr }}
{% endwith %}

看上面这段代码,我们想执行列表的”append”操作,这时使用”{{ arr.append(‘Rainy') }}”页面会输出”None”,换成”{% %}”来执行,程序会报错,因为这是个表达式,不是语句。那怎么办?我们可以启用”jinja2.ext.do”扩展。然后在模板中执行”do”语句即可:

{% with arr = ['Sunny'] %}
 {% do arr.append('Rainy') %}
 {{ arr }}
{% endwith %}

上下文环境
Flask每个请求都有生命周期,在生命周期内请求有其上下文环境Request Context。作为在请求中渲染的模板,自然也在请求的生命周期内,所以Flask应用中的模板可以使用到请求上下文中的环境变量,及一些辅助函数。本文就会介绍下这些变量和函数。
标准上下文变量和函数

请求对象request
request对象可以用来获取请求的方法”request.method”,表单”request.form”,请求的参数”request.args”,请求地址”request.url”等。它本身是一个字典。在模板中,你一样可以获取这些内容,只要用表达式符号”{{ }}”括起来即可。

<p>{{ request.url }}</p>

在没有请求上下文的环境中,这个对象不可用。
会话对象session
session对象可以用来获取当前会话中保存的状态,它本身是一个字典。在模板中,你可以用表达式符号”{{ }}”来获取这个对象。
Flask代码如下,别忘了设置会话密钥哦:

@app.route('/')
def index():
  session['user'] = 'guest'
  return render_template('hello.html')
 
app.secret_key = '123456'

模板代码:

<p>User: {{ session.user }}</p>

在没有请求上下文的环境中,这个对象不可用。
全局对象g
全局变量g,用来保存请求中会用到全局内容,比如数据库连接。模板中也可以访问。
Flask代码:

@app.route('/')
def index():
  g.db = 'mysql'
  return render_template('hello.html')

模板代码:

<p>DB: {{ g.db }}</p>

g对象是保存在应用上下文环境中的,也只在一个请求生命周期内有效。在没有应用上下文的环境中,这个对象不可用。
Flask配置对象config
导入的配置信息,就保存在”app.config”对象中。这个配置对象在模板中也可以访问。

<p>Host: {{ config.DEBUG }}</p>

“config”是全局对象,离开了请求生命周期也可以访问。
url_for()函数
url_for()函数可以用来快速获取及构建URL,Flask也将此函数引入到了模板中,比如下面的代码,就可以获取静态目录下的”style.css”文件。

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

该函数是全局的,离开了请求生命周期也可以调用。
get_flashed_messages()函数
get_flashed_messages()函数是用来获取消息闪现的。这也是一个全局可使用的函数。
自定义上下文变量和函数

自定义变量
除了Flask提供的标准上下文变量和函数,我们还可以自己定义。下面我们就来先定义一个上下文变量,在Flask应用代码中,加入下面的函数:

from flask import current_app
 
@app.context_processor
def appinfo():
  return dict(appname=current_app.name)

函数返回的是一个字典,里面有一个属性”appname”,值为当前应用的名称。我们曾经介绍过,这里的”current_app”对象是一个定义在应用上下文中的代理。函数用”@app.context_processor”装饰器修饰,它是一个上下文处理器,它的作用是在模板被渲染前运行其所修饰的函数,并将函数返回的字典导入到模板上下文环境中,与模板上下文合并。然后,在模板中”appname”就如同上节介绍的”request”, “session”一样,成为了可访问的上下文对象。我们可以在模板中将其输出:

<p>Current App is: {{ appname }}</p>

自定义函数
同理我们可以自定义上下文函数,只需将上例中返回字典的属性指向一个函数即可,下面我们就来定义一个上下文函数来获取系统当前时间:

import time
 
@app.context_processor
def get_current_time():
  def get_time(timeFormat="%b %d, %Y - %H:%M:%S"):
    return time.strftime(timeFormat)
  return dict(current_time=get_time)

我们可以试下在模板中将其输出:

<p>Current Time is: {{ current_time() }}</p>
 <p>Current Day is: {{ current_time("%Y-%m-%d") }}</p>

上下文处理器可以修饰多个函数,也就是我们可以定义多个上下文环境变量和函数。
完整实例:
flask代码:

from flask import Flask, render_template, session, g, current_app
import time
 
app = Flask(__name__)
 
@app.route('/')
def index():
  session['user'] = 'guest'
  g.db = 'mysql'
  return render_template('hello-2.html')
 
@app.context_processor
def appinfo():
  return dict(appname=current_app.name)
 
@app.context_processor
def get_current_time():
  def get_time(timeFormat="%b %d, %Y - %H:%M:%S"):
    return time.strftime(timeFormat)
  return dict(current_time=get_time)
 
app.secret_key = '123456'
 
if __name__ == '__main__':
  app.run(debug=True)

模板代码:

<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 <h1>Hello World!</h1>
 <p>Request URL: {{ request.url }}</p>
 <p>User: {{ session.user }}</p>
 <p>DB: {{ g.db }}</p>
 <p>Host: {{ config.DEBUG }}</p>
 <p>Current App is: {{ appname }}</p>
 <p>Current Time is: {{ current_time() }}</p>
 <p>Current Day is: {{ current_time("%Y-%m-%d") }}</p>
 
{% with messages = get_flashed_messages() %}
 {% if messages %}
  {% for message in messages %}
   Flash Message: {{ message }}</li>
  {% endfor %}
 {% endif %}
{% endwith %}
Python 相关文章推荐
如何准确判断请求是搜索引擎爬虫(蜘蛛)发出的请求
Oct 13 Python
python制作企业邮箱的爆破脚本
Oct 05 Python
Python数据可视化编程通过Matplotlib创建散点图代码示例
Dec 09 Python
Vue的el-scrollbar实现自定义滚动
May 29 Python
详解Django的CSRF认证实现
Oct 09 Python
详解Python3 对象组合zip()和回退方式*zip
May 15 Python
python3 自动识别usb连接状态,即对usb重连的判断方法
Jul 03 Python
pywinauto自动化操作记事本
Aug 26 Python
python多线程使用方法实例详解
Dec 30 Python
Python基于pyjnius库实现访问java类
Jul 31 Python
Python字符串及文本模式方法详解
Sep 10 Python
opencv深入浅出了解机器学习和深度学习
Mar 17 Python
Python的Tornado框架实现异步非阻塞访问数据库的示例
Jun 30 #Python
Python的Tornado框架实现图片上传及图片大小修改功能
Jun 30 #Python
举例讲解Python中metaclass元类的创建与使用
Jun 30 #Python
在Python中定义和使用抽象类的方法
Jun 30 #Python
Python中functools模块的常用函数解析
Jun 30 #Python
深入浅析Python中join 和 split详解(推荐)
Jun 30 #Python
Python列出一个文件夹及其子目录的所有文件
Jun 30 #Python
You might like
PHP正则替换函数preg_replace和preg_replace_callback使用总结
2014/09/22 PHP
ThinkPHP中使用Ueditor富文本编辑器
2015/09/02 PHP
如果文字过长,则将过长的部分变成省略号显示
2006/06/26 Javascript
JavaScript 比较时间大小的代码
2010/04/24 Javascript
css3元素简单的闪烁效果实现(html5 jquery)
2013/12/28 Javascript
HTML5使用DeviceOrientation实现摇一摇功能
2015/06/05 Javascript
Bootstrap 附加导航(Affix)插件实例详解
2016/06/01 Javascript
Vue.js组件tabs实现选项卡切换效果
2016/12/01 Javascript
AngularJS路由实现页面跳转实例
2017/03/03 Javascript
使用npm安装最新版本nodejs
2018/01/18 NodeJs
AngularJs1.x自定义指令独立作用域的函数传入参数方法
2018/10/09 Javascript
JavaScript函数的特性与应用实践深入详解
2018/12/30 Javascript
一次微信小程序内地图的使用实战记录
2019/09/09 Javascript
解决Layui数据表格显示无数据提示的问题
2019/11/14 Javascript
基于vue和bootstrap实现简单留言板功能
2020/05/30 Javascript
Vue-cli 移动端布局和动画使用详解
2020/08/10 Javascript
vue 接口请求地址前缀本地开发和线上开发设置方式
2020/08/13 Javascript
[06:21]2014DOTA2国际邀请赛 庆祝VG首阶段领跑;B叔为挣牛排半夜整理情报
2014/07/13 DOTA
python 禁止函数修改列表的实现方法
2017/08/03 Python
Python3使用正则表达式爬取内涵段子示例
2018/04/22 Python
Python WSGI的深入理解
2018/08/01 Python
使用Python自动化破解自定义字体混淆信息的方法实例
2019/02/13 Python
Python中的引用知识点总结
2019/05/20 Python
python实现得到当前登录用户信息的方法
2019/06/21 Python
Python中无限循环需要什么条件
2020/05/27 Python
波兰灯具、照明和LED购物网站:Lampy.pl
2019/03/11 全球购物
总裁岗位职责
2013/12/04 职场文书
公益活动策划方案
2014/01/09 职场文书
安全生产演讲稿
2014/05/09 职场文书
社团活动总结格式
2014/08/29 职场文书
机关作风建设剖析材料
2014/10/11 职场文书
银行招聘自荐信
2015/03/06 职场文书
工作服管理制度范本
2015/08/06 职场文书
导游词之青城山景区
2019/09/27 职场文书
《雪域豹影》读后感:父爱的伟大
2019/12/23 职场文书
Linux中Nginx的防盗链和优化的实现代码
2021/06/20 Servers