flask模板的基本用法(定界符、模板语法、渲染模板),模板辅助工具(上下文、全局对象、过滤器、测试器、模板环境对象)
flask模板
在动态web程序中,视图函数返回的HTML数据往往需要根据相应的变量(比如查询参数)动态生成。
当HTML代码保存到单独的文件中时,我们没法再使用字符串格式化或拼接字符串的当时在HTML代码中插入变量,这时我们需要使用模板引擎(template engine)。借助模板引擎,我们可以再HTML文件中使用特殊的语法来标记变量,这类包含固定内容和动态部分的可重用文件称为模板(template)。
模板引擎的作用就是读取并执行模板中的特殊语法标记,并根据传入的数据将变量替换为实际值,输出最终的HTML页面,这个过程被称为渲染(rendering)。
Flask默认使用的模板引擎是jinja2,他是一个功能齐全的python模板引擎,输了设置变量,还允许我们在模板中添加if判断,执行for迭代,调整函数等,以各种方式 控制模板的输出。
对于jinja2来说,模板可以是任何格式的纯文本文件,比如HTML、XML、CSV等。
模板的基本用法
下面介绍一下如何使用jinja创建HTML模板,并在视图函数中渲染模板,最终实现HTML响应的动态化
创建模板
假设我们需要编写一个用户的电影清单页面,模板中需要显示用户信息以及用户收藏的电影列表,包含电影的名字和年份。首先创建一些虚拟数据用于测试显示效果:
user = {
'username': 'Grey Li',
'bio': 'A boy who loves movies and music.'
}
movies = [
{'name' : 'My Neighbor Totoro','year':'1988'},
{'name': 'Three Colours trilogy', 'year': '1993'},
{'name': 'Forrest Gump', 'year': '1994'},
{'name': 'Perfect Blue', 'year': '1997'},
{'name': 'The Matrix', 'year': '1999'},
{'name': 'Memento', 'year': '2000'},
{'name': 'The Bucket list', 'year': '2007'},
{'name': 'Black Swan', 'year': '2010'},
{'name': 'Gone Girl', 'year': '2014'},
{'name': 'CoCo', 'year': '2017'}
我们在templates目录下创建一个watchlist.html作为模板文件,然后使用jinja2支持的语法在模板中操作这些变量。
template/watchlist.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ user.username }}'s Watchlist</title>
</head>
<body>
<a href = "{{ url_for('hello') }}">← Return</a>
<h2>{{ user.username }}</h2>
{% if user.bio %}
<i>{{ user.bio }}</i>
{% else %}
<i>This user has not provided a bio.</i>
{% endif %}
{# 下面是电影清单(这是注释) #}
<h5>{{ user.username }}'s Watchlist ({{ movies[length] }}):</h5>
<ul>
{% for movie in movies %}
<li>{{ movie.name }} - {{ movie.year }}</li>
{% endfor %}
</ul>
</body>
</html>
在模板中使用的&larr;是HTML实体,HTML实体除了用来转义HTML保留保留符号外,通常会被用来显示不容易通过键盘输入的字符。这里的←会显示为左箭头,另外,©用来显示版权标志。
在模板中添加python语句和表达式时,需要使用特定的定界符把他们标示出来。watchlist.html中设计的模板语法,我们会在下面逐一介绍。首先,可以看到上面的代码中看到Jinja2里常见的三种定界符:
常见的三种定界符
1、 语句
比如if判断、for循环等:
{% … %}
2、 表达式
比如字符串、变量、函数调用等:
{{ … }}
3、 注释
{# … #}
另外,在模板中,Jinja2支持使用“.”获取变量的属性,比如user字典中的username键值通过“.”获取,即user.username,在效果上等同于user[‘username’]。
模板语法
利用jinja2这样的模板引擎,我们可以将一部分的程序逻辑放到模板中去。简单地说,我们可以在模板中使用python语句和表达式来操作数据的输出。但需要注意的是,jinja2并不支持所有python语法。并且出于效率和代码组织等方面的考虑,我们应该适度使用模板,仅把和输出控制有关的逻辑操作放到模板中。
jinja2允许你在模板中使用大部分python对象,比如字符串、列表、字典、元组、整型、浮点型、布尔值。它支持基本的运算符号(+、-、*、/等)、比较符号(比如==、!=等)、逻辑符号(and、or、not和括号)以及in、is、None和布尔值(True、False)。
jinja2提供了多种控制结构来控制模板的输出,其中for和if是最常用的两种。jinja2里,语句使用{% … %}表示,尤其需要注意的是,在语句结束的地方,必须添加结束标签:
{% if user.bio %}
<i>{{ user.bio }}</i>
{% else %}
<i>This user has not provided a bio.</i>
{% endif %}
在这个if语句里,如果user.bio已经定义,就渲染{%if user.bio%}和{%else%}之间的内容,否则就渲染{%else%}和{%endif%}之间的默认内容。末尾的{%endif%}用来声明if语句的结束,这一行不能省略。
和python里一样,for语句用来迭代一个序列:
<ul>
{% for movie in movies %}
<li>{{ movie.name }} - {{ movie.year }}</li>>
{% endfor %}
</ul>>
和其他语句一样,你需要在for循环的结尾使用endfor标签声明for语句的结束。在for循环内,jinja2提供了多个特殊变量,常用的for循环变量如图:
渲染模板
渲染一个模板,就是执行模板的代码,并传入所有在模板中使用的变量,渲染后的结果就是我们要返回给客户端的HTML响应。在视图函数中渲染模板时,我们并不直接使用jinja2提供的函数,而是使用flask提供的渲染函数render_template()
from flask import Flask,render_template
@app.route('/watchlist')
def watchlist():
return render_template('watchlist.html',user=user,movies = movies)
在render_template()函数中,我们首先传入的模板的文件名作为参数。Flask会在程序根目录下的templates文件夹里寻找模板文件,所以这里传入的文件路径是相对于templates根目录的。除了模板文件路径,我们还以关键字参数的形式传入了模板中使用的变量值,以user为里:左边的user表示传入模板的变量名称,右边的user则是要传入的对象。
除了render_template()函数,Flask还提供了一个render_template_string()函数用来渲染模板字符串。
其他类型的变量通过相同的方式传入。传入jinja2中的变量值可以是字符串、列表和字典,也可以是函数、类和类实例,这完全取决于你在视图函数传入的值。
例如:
<p>这时列表my_list的第一个元素:{{ my_list[0] }}</p>
<p>这是元祖my_tuple的第一个元素:{{ my_tutple[0] }}</p>
<p>这是字典my_dict的键为name的值:{{ my_dict[‘name’] }}</p>
<p>这是函数my_func的返回值:{{ my_func() }}</p>
<p>这是对象my_object调用某方法的返回值:{{ my_object.name() }}</p>
如果你想传入函数在模板中调用,那么需要传入函数对象本身,而不是函数调用(函数的返回值),所以近些出函数名称即可。当把函数传入模板后,我们可以像在python脚本中一样通过添加括号的方式调用,而且你也可以在括号中传入参数。
根据我们传入的虚拟数据,render_template()渲染后返回的HTML数据如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Grey Li's Watchlist</title>
</head>
<body>
<a href = "/hello">← Return</a>
<h2>Grey Li</h2>
<i>A boy who loves movies and music.</i>
<h5>Grey Li's Watchlist ():</h5>
<ul>
<li>My Neighbor Totoro - 1988</li>
<li>Three Colours trilogy - 1993</li>
<li>Forrest Gump - 1994</li>
<li>Perfect Blue - 1997</li>
<li>The Matrix - 1999</li>
<li>Memento - 2000</li>
<li>The Bucket list - 2007</li>
<li>Black Swan - 2010</li>
<li>Gone Girl - 2014</li>
<li>CoCo - 2017</li>
</ul>>
</body>
</html>
在和渲染前的模板文件对比时你会发现,原模板中所有的jinja语句、表达式、注释都会在执行后被移除,而所有的变量都会替换为对应的数据,访问127.0.0.7:5000/watchlist即可以看到渲染后的页面:
模板辅助工具
除了基本语法,jinja2还提供了许多方便的工具,这些工具可以让你更方便的控制模板的输出。为了方便测试,我们在示例程序的templates目录下创建了一个根页面模板index.html。返回主页的index视图和watchlist视图类似:
from flask import render_template @app.route('/')
def index():
return render_template('index.html')
上下文
模板上下文包含了很多变量,其中包含我们调用render_template()函数时手动传入的变量以及flask默认传入的变量。
除了渲染时传入变量,也可以在模板中定义变量,使用set标签:
{% set navigation = [(‘/’, ‘Home’), (‘/about’, ‘About’)]%}
你也可以将一部分模板数据定义为变量,使用set和endset标签声明开始和结束:
{% set navigation %}
<li><a href="/">Home</a></li>
<li><a>href="/about"></a></li>
{% endset %}
内置上下文变量
Flask在模板上下文中提供了一些内置变量,可以在模板中直接使用
自定义上下文
如果多个模板都需要使用同一变量,那么比起在多个视图函数中重复传入,更好的办法是能够设置一个模板全局变量。flask提供了一个app.context_processor装饰器,可以用来注册模板上下文处理函数,它可以帮我们完成统一传入变量的工作。模板上下文处理函数需要返回一个包含变量键值对的字典
注册模板上下文处理函数:
@app.context_processor
def inject_foo():
foo = 'I am foo.'
return dict(foo=foo)#等同于return {'foo': foo}
当我们调用render_remplate()函数渲染任意一个模板时,所有使用app.context_processor装饰器注册的模板上下文处理函数(包括flask内置的上下文处理函数)都会被执行,这些函数的返回值会被添加到模板中,因此我们可以在模板中直接使用foo变量。
和在render_remplate()函数中传入变量类似,除了字符串、列表等数据结构,你也可以传入函数、类或类实例。
除了使用app.context_processor装饰器,也可以直接将其作为方法调用,传入模板上下文处理函数。
def inject_foo():
foo = "I am foo."
return dict(foo=foo) app.context_processor(inject_foo)
使用lambda可以简化为:
app.context_processor(lambda:dict(foo='I am foo.'))
全局对象
全局对象是指在所有的模板中都可以直接使用的对象,包括在模板中导入的模板
设置全局函数
jinja2在模板中默认提供了一些全局函数,常用的三个函数:
除了jinja2内置的全局函数,flask也在模板中内置了两个全局函数
flask除了把g、session、config、request对象注册上下文变量,也将他们设置为全局变量,因此可以全局使用。
url_for()用来获取URL,用法和在python脚本相同。在前面给出的watchlist.html模板中,用来返回主页的链接直接写出。在实际的代码中,这个URL使用url_for()生成,传入index视图的端点(index):
<a href=”{{ url_for(‘index’) }}”>← Return</a>
自定义全局函数
除了使用app.context_processor注册模板上下文处理函数来传入变量,我们也可以使用app.template_global装饰器直接将函数注册为模板全局函数。比如,下面例子把bar()函数注册为模板全局函数。
@app.template_global()
def bar():
return 'I am bar.'
默认使用函数的原名称传入模板,在app.template_global()装饰器中使用name参数可以指定一个自定义名称。app.template_global()仅能用于注册全局函数。
你可以直接使用app.add_template_global()方法注册自定义全局函数,传入函数对象和可选的自定义名称(name),比如app.add_template_global(your_global_function)。
过滤器
在jinja2中,过滤器(filter)是一些可以用来修改和过滤变量值的特殊函数,过滤器和变量用一个数显(管道符号)隔开,需要参数的过滤器可以像函数一样使用括号传递。
下面是一个堆name变量使用title过滤器的例子:
{{ name|title }}
这会将name变量的值标题化,相当于在python里调用name.title()。再比如,在前面的示例模板watchlist.html中使用length获取movies列表的长度,类似于在python中调用len(movies):
{{ movies|length }}
另一种用法是将过滤器作用于一部分模板数据,使用filter标签和endfilter标签声明开始和结束。比如,下面是用upper过滤器讲一段文字转换为大写:
{% filter upper %}
This text becomes uppercase.
{% endfilter % }
内置过滤器
jinja2提供了许多内置过滤器,常用的过滤器有:
在使用过滤器时,列表中过滤器函数的第一个参数表示被过滤的变量值(value)或字符串(s),即竖线符号左侧的值,其他的参数可以通过添加括号传入。
另外,过滤器可以叠加使用,下面的示例为name变量设置默认值,并将其标题化:
<h1>Hello, {{ name|default('陌生人')|title }}!</h1>
在之前学习XSS攻击的防范措施时,主要是对用户输入的文本进行转义,根据flask的设置,jinja2会自动对模板中的变量进行转义,所以我们不用手动使用escape过滤器或调用escape()函数对变量进行转义。
默认的自动公开其转义仅针对.html、.htm、.xml以及.xtml后缀的文件,用于渲染模板字符串的render_template_string()函数也会对所有传入的字符串进行转义。
在确保变量值安全的情况下,如果你想避免转义,将变量作为HTML解析,可以对变量使用safe过滤器:
{{ santitized_text|safe }}
另一种将文本记为安全的方法是在渲染前将变量转换为markup对象:
from fflask import Markup @app.route('/hello6')
def hello6():
text = Markup('<h>Hello, Flask!</h1>')
return render_template('index.html',text=text)
这时在模板中可以直接使用{{ text }}
绝对不要直接对用户输入的内容使用safe过滤器,否则容易被植入恶意代码,导致XSS攻击。
自定义过滤器
如果内置的过滤器不能满足你的需要,还可以添加自定义过滤器。使用app.template_filter()装饰器可以注册自定义过滤器
例子:
from flask import Markup @app.template_filter()
def musical(s):
return s + Markup(' ♫')
和注册全局函数类似,你可以在app.template_filter()中使用name关键字设置过滤器的名称,默认会使用函数名称。过滤器函数需要接收被处理的值作为输入,返回处理后的值。过滤器函数接收s作为过滤的变量值,返回处理后的值。我们创建的musical过滤器会在被过滤的变量字符后边添加一个音符图标,因为音符通过HTML实体♫表示,我们使用Markup类将它标记为圈圈字符。在使用时和其他过滤器用法相同:
{{ name|musical }}
你可直接一个app.add_template_filter()方法注册自定义过滤器,传入函数对象和可选的自定义名称(name),比如app.add_template_filter(your_filer_function)。
测试器
在jinja2中,测试器(Test)是一些用来测试变量或表达式,返回布尔值(True或False)的特殊函数。比如,number测试器用来判断一个变量或表达式是否是数字,我们使用is连接变量和测试器:
{% if age is number %}
{{ age * 365 }}
{% else %}
无效的数字
{% endif %}
内置测试器
jinja2内置了许多测试器,常用的测试器及用法说明如下表:
在使用测试器时,is的左侧是测试器函数的第一个参数(value),其他参数可以添加括号传入,也可以在右侧使用空格连接,以sames为例:
{% if foo is sameas(bar) %}…
等同于:
{% if foo is samesas bar %}…
自定义测试器
和过滤器类似,我们可以使用flask提供的app.template_test()装饰器来注册一个自定义测试器。下例中,我们创建一个baz过滤器,用来验证被测值是否为baz:
@app.template_test()
def baz(n):
if n == 'baz':
return True
return False
测试器的名称默认为函数名称,你可以在app.template_test()中使用name关键字指定自定义名称。测试器函数需要接收被测试的值作为输入,返回布尔值。
你可以直接使用app.add_template_test()方法注册自定义测试器,传入函数对象和可选的自定义名称(name),比如app.add_template_test(your_test_function)。
模板环境对象
在jinja2中,渲染行为由jinja2.Environment类控制,所有的配置选项、上下文变量、全局函数、过滤器和测试器都存储在Environment实例上。当于flask结合后,我们并不单独创建Environment对象,而是使用flask创建的Environment对象,它存储在app.jinja_env属性上。
在程序中,我们可以使用app.jinja_env更改jinja2设置。比如,可以自定义所有的定界符。下面使用variable_start_string和variable_end_string分别自定义变量定界符的开始和结束符号:
app = Flask(__name__)
app.jinja_env.variable_start_string = ’[[’
app.jinja_env.variable_end_string = ‘}}’
在实际开发中,如果修改jinja2的定界符,那么需要注意与扩展提供模板的兼容问题,一般不建议修改。
模板环境中的全局函数、过滤器和测试器分别存储在Environment对象的globals、filters和tests属性中,这三个属性都是字典对象。除了使用flask提供的装饰器和方法注册自定义函数,我们也可以直接操作这三个字典添加相应的函数或变量,这通过向对应的字典属性中添加一个键值对实现,把名称作为键传入模板,对应的函数对象或变量作为值。下面是几个简单的例子。
添加自定义全局对象
使用app.jinja_env.globals分别向模板中添加全局函数bar和局部变量foo:
def bar():
return "I am bar"
foo = "i am foo"
app.jinja_env.globals['bar'] = bar
app.jinja_env.globals['foo'] = foo
添加自定义过滤器
使用app.jinja_env.filters向模板中添加自定义过滤器smiling:
def smiling(s):
return s + ' :'
app.jinja_env.filters['smiling'] = smiling
添加自定义测试器
使用app.jinja_env.tests向模板中添加自定义测试器baz
def baz(n):
if n == 'baz':
return True
return False
app.jinja_env.tests['baz'] = baz
flask模板的基本用法(定界符、模板语法、渲染模板),模板辅助工具(上下文、全局对象、过滤器、测试器、模板环境对象)的更多相关文章
- ThinkPHP模板IF标签用法详解
投稿:shichen2014 字体:[增加 减小] 类型:转载 时间:2014-07-01 我要评论 这篇文章主要介绍了ThinkPHP模板IF标签用法,需要的朋友可以参考下 ThinkPHP的IF标 ...
- Django学习笔记(3)——表单,测试和模板语法的学习
一,表单form 为了接收用户的投票选择,我们需要在前段页面显示一个投票界面,让我们重写之前的polls/detail.html文件,代码如下: <h1>{{ question.quest ...
- Handlebars的基本用法 Handlebars.js使用介绍 http://handlebarsjs.com/ Handlebars.js 模板引擎 javascript/jquery模板引擎——Handlebars初体验 handlebars.js 入门(1) 作为一名前端的你,必须掌握的模板引擎:Handlebars 前端数据模板handlebars与jquery整
Handlebars的基本用法 使用Handlebars,你可以轻松创建语义化模板,Mustache模板和Handlebars是兼容的,所以你可以将Mustache导入Handlebars以使用 Ha ...
- C++(VS2015)模板显式特化之template语法深入理解
首先说下遇到的情况: 这里在vc++6.0上建立了一个自定义模板类,再去覆盖这个类,分别使用部分覆盖,整体覆盖 但在vs2015上去整体覆盖类会报错. 错误如下: 错误原因:个人感觉是新版本的vs更接 ...
- Flask入门模板过滤器与测试器(五)
1 模板引擎之过滤器 概念 : 过滤器本质上是个转换函数,第一个参数是待过滤的变量.如果它有第二个参数,模板中就必须传进去. 过滤器使用管道符| 放在{{ }} Jinja2模板引擎提供了丰富的内置过 ...
- C++学习8-面向对象编程基础(模板)
模板 模板是一种工具,模板可以使程序员能建立具有通用类型的函数库与类库: 模板具有两种不同的形式: 函数模板 类模板 函数模板 当一个add()函数接收两个参数,因为某种特定情况,所传入的实参数据类型 ...
- Flask07 Jinja2模板测试器、控制语句IF/FOR、变量/块 赋值、作用域、块级作用域
1 测试器及其使用 在模板中的 {{}} 可以书写测试器,格式如下 {{ 变量 is 测试器名称 }} 1.1 在python中导入 Jinja2 的模板 from jinja2 import te ...
- 不可或缺 Windows Native (19) - C++: 对象的动态创建和释放, 对象的赋值和复制, 静态属性和静态函数, 类模板
[源码下载] 不可或缺 Windows Native (19) - C++: 对象的动态创建和释放, 对象的赋值和复制, 静态属性和静态函数, 类模板 作者:webabcd 介绍不可或缺 Window ...
- sublime Emmet的用法及相关语法
本节来讲一下Emmet插件的用法及相关语法. Emmet插件极大的提高了编程员的编程速度,下面我们来讲讲它的具体语法: 一.生成 HTML 文档初始结构 HTML 文档的初始结构,就是包括 docty ...
随机推荐
- 关于flexjson将json转为javabean的使用
关于flexjson将json转为javabean的使用 import java.sql.Timestamp; import java.util.Date; import flexjson.JSOND ...
- Spring MVC中前端控制器拦截问题
<!-- 前端控制器 --> <servlet> <servlet-name>ssm</servlet-name> <servlet-class& ...
- MongoDB的客户端管理工具--nosqlbooster 连接MongoDB服务器
nosqlbooster的官网地址为https://nosqlbooster.com.大家如果想直接下载,可以登入下载网址https://nosqlbooster.com/downloads. 下载w ...
- React篇-报错信息:warning: Can't call setState (or forceUpdate) on an unmounted component.
报错信息是: Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but ...
- vant - 头部 - header【Layout 布局】【Icon 图标】
安装 npm i vant -S [main.js] import Vant from 'vant'; import 'vant/lib/index.css'; Vue.use(Vant); [ind ...
- field-symbols: <ATTR> type ANY.
field-symbols: type ANY. * importing iv_root_list type refer to if_genil_cont_root_objectlist DATA l ...
- nvm管理node之后,安装npm包出现的问题
在学习Node.js连接MySQL时,使用nvm安装node6.10.0,在项目目录下npm install mysql 出问题. 在查询解决方法后,得知是因为配置文件有问题 package.json ...
- IdentityServer4:IdentityServer4+API+Client+User实践OAuth2.0密码模式(2)
一.密码模式实操 仍然使用第一节的代码:做如下改动: 1.授权服务端 前面我们使用项目:Practice.IdentityServer作为授权服务器 修改项目的Config.cs类: 添加测试用户,并 ...
- 从零开始一起学习SLAM | 为啥需要李群与李代数?
很多刚刚接触SLAM的小伙伴在看到李群和李代数这部分的时候,都有点蒙蒙哒,感觉突然到了另外一个世界,很多都不自觉的跳过了,但是这里必须强调一点,这部分在后续SLAM的学习中其实是非常重要的基础,不信你 ...
- cocos2d JS 鼠标响应事件
对于PC和超级本,添加鼠标事件的的处理,可以加强用户的体验,其处理逻辑与触摸事件基本一样,多了一些鼠标特有的事件响应 如滚轮事件(onMouseScroll) cc.eventManager.addL ...