赵走x博客
网站访问量:151929
首页
书籍
软件
工具
古诗词
搜索
登录
Flask 实战:41、电子邮件进阶实践
Flask 实战:40、使用事务邮件服务SendGird
Flask 实战:39、使用Flask-Mail发送电子邮件
Flask 实战:38、数据库进阶实践
Flask 实战:37、更新数据库表
Flask 实战:36、定义关系
Flask 实战:35、在视图函数里操作数据库
Flask 实战:34、数据库操作:CRUD
Flask 实战:33、使用Flask-SQLAlchemy管理数据库
Flask 实战:32、ORM魔法
Flask 实战:31、数据库的分类
Flask 实战:30、单个页面多个表单
Flask 实战:29、单个表单多个提交按钮
Flask 实战:28、使用Flask-CKEditor集成富文本编辑器
Flask 实战:27、多文件上传
Flask 实战:26、文件上传
Flask 实战:25、自定义验证器
Flask 实战:24、使用宏渲染表单
Flask 实战:23、设置错误消息语言
Flask 实战:22、处理表单数据
Flask 实战:21、使用Flask-WTF处理表单
Flask 实战:20、HTML表单
Flask 实战:19、模板进阶实践
Flask 实战:18、模板结构组织
Flask 实战:17、模板辅助工具
Flask 实战:16、模板基本用法
Flask 实战:15、HTTP进阶实践
Flask 实战:14、Flask上下文
Flask 实战:13、HTTP响应
Flask 实战:12、HTTP请求
Flask 实战:11、请求响应循环
Flask 实战:10、Flask与MVC架构
Flask 实战:9、模板与静态文件
Flask 实战:8、Flask命令
Flask 实战:7、URL与端点
Flask 实战:6、项目配置
Flask 实战:5、Flask扩展
Flask 实战:4、Python Shell
Flask 实战:3、启动开发服务器
Flask 实战:2、Hello,Flask!
Flask 实战:1、初识Flask
Flask 实战:17、模板辅助工具
资源编号:75855
Python Web
Flask Web开发实战:入门、进阶与原理解析
热度:78
除了基本语法,Jinja2还提供了许多方便的工具,这些工具可以让你更方便地控制模板的输出。
除了基本语法,Jinja2还提供了许多方便的工具,这些工具可以让你更方便地控制模板的输出。 为了方便测试,我们在示例程序的templates目录下创建了一个根页面模板index.html。返回主页的index视图和watchlist视图类似: ``` from flask import render_template @app.route('/') def index(): return render_template('index.html') ``` # 1、上下文 模板上下文包含了很多变量,其中包括我们调用render_template()函数时手动传入的变量以及Flask默认传入的变量。 除了渲染时传入变量,你也可以在模板中定义变量,使用set标签: ``` {% set navigation = [('/', 'Home'), ('/about', 'About')] %} ``` 你也可以将一部分模板数据定义为变量,使用set和endset标签声明开始和结束: ``` {% set navigation %}
Home
About
{% endset %} ``` ### 1.内置上下文变量 Flask在模板上下文中提供了一些内置变量,可以在模板中直接使用,如表3-2所示。  表3-2 标准模板全局变量 ### 2.自定义上下文 如果多个模板都需要使用同一变量,那么比起在多个视图函数中重复传入,更好的方法是能够设置一个模板全局变量。Flask提供了一个app.context_processor装饰器,可以用来注册模板上下文处理函数,它可以帮我们完成统一传入变量的工作。模板上下文处理函数需要返回一个包含变量键值对的字典,如代码清单所示。 ``` @app.context_processor def inject_foo(): foo = 'I am foo.' return dict(foo=foo) # 等同于return {'foo': foo} ``` 当我们调用render_template()函数渲染任意一个模板时,所有使用app.context_processor装饰器注册的模板上下文处理函数(包括Flask内置的上下文处理函数)都会被执行,这些函数的返回值会被添加到模板中,因此我们可以在模板中直接使用foo变量。 和在render_template()函数中传入变量类似,除了字符串、列表等数据结构,你也可以传入函数、类或类实例。 除了使用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.')) ``` # 2、全局对象 全局对象是指在所有的模板中都可以直接使用的对象,包括在模板中导入的模板,后面我们会详细介绍导入的概念。 ### 1.内置全局函数 Jinja2在模板中默认提供了一些全局函数,常用的三个函数如表3-3所示。  表3-3 Jinja2内置模板全局函数 这里只列出了部分常用的全局函数,完整的全局函数列表请访问[http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions](http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions) 查看。 除了Jinja2内置的全局函数,Flask也在模板中内置了两个全局函数,如表3-4所示。  表3-4 Flask内置模板全局函数 Flask除了把g、session、config、request对象注册为上下文变量,也将它们设为全局变量,因此可以全局使用。 url_for()用来获取URL,用法和在Python脚本中相同。在前面给出的watchlist.html模板中,用来返回主页的链接直接写出。在实际的代码中,这个URL使用url_for()生成,传入index视图的端点: ```
← Return
``` get_flashed_messages()的用法我们会在后面介绍。 ### 2.自定义全局函数 除了使用app.context_processor注册模板上下文处理函数来传入函数,我们也可以使用app.template_global装饰器直接将函数注册为模板全局函数。比如,代码清单把bar()函数注册为模板全局函数: ``` @app.template_global('bar') 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)。 # 3、过滤器 在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 %} ``` ### 1.内置过滤器 Jinja2提供了许多内置过滤器,常用的过滤器如表3-5所示。   表3-5 Jinja2常用内置过滤器 这里只列出了一部分常用的过滤器,完整的列表请访问[http://jinja.pocoo.org/docs/2.10/templates/#builtin-filters](http://jinja.pocoo.org/docs/2.10/templates/#builtin-filters) 查看。 在使用过滤器时,列表中过滤器函数的第一个参数表示被过滤的变量值(value)或字符串(s),即竖线符号左侧的值,其他的参数可以通过添加括号传入。 另外,过滤器可以叠加使用,下面的示例为name变量设置默认值,并将其标题化: ```
Hello, {{ name|default('陌生人')|title }}!
``` 在第2章,我们介绍了XSS攻击的主要防范措施,其中最主要的是对用户输入的文本进行转义。根据Flask的设置,Jinja2会自动对模板中的变量进行转义,所以我们不用手动使用escape过滤器或调用escape()函数对变量进行转义。 >提示 默认的自动开启转义仅针对.html、.htm、.xml以及.xhtml后缀的文件,用于渲染模板字符串的render_template_string()函数也会对所有传入的字符串进行转义。 在确保变量值安全的情况下,这通常意味着你已经对用户输入的内容进行了“消毒”处理。这时如果你想避免转义,将变量作为HTML解析,可以对变量使用safe过滤器: ``` {{ sanitized_text|safe }} ``` 另一种将文本标记为安全的方法是在渲染前将变量转换为Markup对象: ``` from flask import Markup @app.route('/hello') def hello(): text = Markup('
Hello, Flask!
') return render_template('index.html', text=text) ``` 这时在模板中可以直接使用{{text}}。 >注意 绝对不要直接对用户输入的内容使用safe过滤器,否则容易被植入恶意代码,导致XSS攻击。 ### 2.自定义过滤器 如果内置的过滤器不能满足你的需要,还可以添加自定义过滤器。使用app.template_filter()装饰器可以注册自定义过滤器,代码清单注册了一个musical过滤器。 ``` from flask import Markup @app.template_filter('musical') def musical(s): return s + Markup(' ♫') ``` 和注册全局函数类似,你可以在app.template_filter()中使用name关键字设置过滤器的名称,。过滤器函数需要接收被处理的值作为输入,返回处理后的值。过滤器函数接收s作为被过滤的变量值,返回处理后的值。我们创建的musical过滤器会在被过滤的变量字符后面添加一个音符(single bar note)图标,因为音符通过HTML实体♫;表示,我们使用Markup类将它标记为安全字符。在使用时和其他过滤器用法相同: ``` {{ name|musical }} ``` 你可以直接使用app.add_template_filter()方法注册自定义过滤器,传入函数对象和可选的自定义名称(name),比如app.add_template_filter(your_filter_function)。 # 4、测试器 在Jinja2中,测试器(Test)是一些用来测试变量或表达式,返回布尔值(True或False)的特殊函数。比如,number测试器用来判断一个变量或表达式是否是数字,我们使用is连接变量和测试器: ``` {% if age is number %} {{ age * 365 }} {% else %} 无效的数字。 {% endif %} ``` ### 1.内置测试器 Jinja2内置了许多测试器,常用的测试器及用法说明如表3-6所示。  表3-6 常用的内置测试器 这里只列出了一部分常用的测试器,完整的内置测试器列表请访问[http://jinja.pocoo.org/docs/2.10/templates/#list-of-builtin-tests](http://jinja.pocoo.org/docs/2.10/templates/#list-of-builtin-tests) 查看。 在使用测试器时,is的左侧是测试器函数的第一个参数(value),其他参数可以添加括号传入,也可以在右侧使用空格连接,以sameas为例: ``` {% if foo is sameas(bar) %}... ``` 等同于: ``` {% if foo is sameas bar %}... ``` ### 2.自定义测试器 和过滤器类似,我们可以使用Flask提供的app.template_test()装饰器来注册一个自定义测试器。在示例程序中,我们创建了一个没有意义的baz过滤器,仅用来验证被测值是否为baz,如代码清单所示: ``` @app.template_test('baz') 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)。 # 5、模板环境对象 在Jinja2中,渲染行为由jinja2.Enviroment类控制,所有的配置选项、上下文变量、全局函数、过滤器和测试器都存储在Enviroment实例上。当与Flask结合后,我们并不单独创建Enviroment对象,而是使用Flask创建的Enviroment对象,它存储在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的定界符,那么需要注意与扩展提供模板的兼容问题,一般不建议修改。 模板环境中的全局函数、过滤器和测试器分别存储在Enviroment对象的globals、filters和tests属性中,这三个属性都是字典对象。除了使用Flask提供的装饰器和方法注册自定义函数,我们也可以直接操作这三个字典来添加相应的函数或变量,这通过向对应的字典属性中添加一个键值对实现,传入模板的名称作为键,对应的函数对象或变量作为值。下面是几个简单的示例。 ### 1.添加自定义全局对象 和app.template_global()装饰器不同,直接操作globals字典允许我们传入任意Python对象,而不仅仅是函数,类似于上下文处理函数的作用。下面的代码使用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 ``` ### 2.添加自定义过滤器 下面的代码使用app.jinja_env.filters向模板中添加自定义过滤器smiling: ``` def smiling(s): return s + ' :)' app.jinja_env.filters['smiling'] = smiling ``` ### 3.添加自定义测试器 下面的代码使用app.jinja_env.tests向模板中添加自定义测试器baz: ``` def baz(n): if n == 'baz': return True return False app.jinja_env.tests['baz'] = baz ``` 访问[http://jinja.pocoo.org/docs/latest/api/#jinja2.Environment](http://jinja.pocoo.org/docs/latest/api/#jinja2.Environment) 查看Enviroment类的所有属性及用法说明。