赵走x博客
网站访问量:151849
首页
书籍
软件
工具
古诗词
搜索
登录
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 实战:19、模板进阶实践
资源编号:75866
Python Web
Flask Web开发实战:入门、进阶与原理解析
热度:92
这一节我们会介绍模板在Flask程序中的常见应用,其中主要包括加载静态文件和自定义错误页面。
这一节我们会介绍模板在Flask程序中的常见应用,其中主要包括加载静态文件和自定义错误页面。 # 1、空白控制 在实际输出的HTML文件中,模板中的Jinja2语句、表达式和注释会保留移除后的空行,前面为了节省篇幅手动删掉了这些空行。以示例程序中的这段代码为例: ``` {% if user.bio %}
{{ user.bio }}
{% else %}
This user has not provided a bio.
{% endif %} ``` Jinja2语句中的HTML代码缩进并不是必须的,只是为了增加可读性,在编写大量Jinja2代码时可读性尤其重要。 实际输出的HTML代码如下所示: ```
{{ user.bio }}
This user has not provided a bio.
``` 如果你想在渲染时自动去掉这些空行,可以在定界符内侧添加减号。比如,{%-endfor%}会移除该语句前的空白,同理,在右边的定界符内侧添加减号将移除该语句后的空白: ``` {% if user.bio -%}
{{ user.bio }}
{% else -%}
This user has not provided a bio.
{%- endif %} ``` 现在输出的HTML代码如下所示: ```
{{ user.bio }}
This user has not provided a bio.
``` 你可以访问[http://jinja.pocoo.org/docs/latest/templates/#whitespace-control](http://jinja.pocoo.org/docs/latest/templates/#whitespace-control) 查看更多细节。 除了在模板中使用减号来控制空白外,我们也可以使用模板环境对象提供的trim_blocks和lstrip_blocks属性设置,前者用来删除Jinja2语句后的第一个空行,后者则用来删除Jinja2语句所在行之前的空格和制表符(tabs): ``` app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True ``` trim_blocks中的block指的是使用{%...%}定界符的代码块,与我们前面介绍模板继承中的块无关。 需要注意的是,宏内的空白控制行为不受trim_blocks和lstrip_blocks属性控制,我们需要手动设置,比如: ``` {% macro qux(amount=1) %} {% if amount == 1 -%} I am qux. {% elif amount > 1 -%} We are quxs. {%- endif %} {% endmacro %} ``` 事实上,我们没有必要严格控制HTML输出,因为多余的空白并不影响浏览器的解析。在部署时,我们甚至可以使用工具来去除HTML响应中所有的空白、空行和换行,这样可以减小文件体积,提高数据传输速度。所以,编写模板时应以可读性为先,在后面的示例程序中,我们将不再添加空白控制的代码,并且对Jinja2语句中的HTML代码进行必要的缩进来增加可读性。 # 2、加载静态文件 一个Web项目不仅需要HTML模板,还需要许多静态文件,比如CSS、JavaScript文件、图片以及音频等。在Flask程序中,默认我们需要将静态文件存储在与主脚本(包含程序实例的脚本)同级目录的static文件夹中。 为了在HTML文件中引用静态文件,我们需要使用url_for()函数获取静态文件的URL。Flask内置了用于获取静态文件的视图函数,端点值为static,它的默认URL规则为/static/
,URL变量filename是相对于static文件夹根目录的文件路径。 如果你想使用其他文件夹来存储静态文件,可以在实例化Flask类时使用static_folder参数指定,静态文件的URL路径中的static也会自动跟随文件夹名称变化。在实例化Flask类时使用static_url_path参数则可以自定义静态文件的URL路径。 在示例程序的static目录下保存了一个头像图片avatar.jpg,我们可以通过url_for('static',filename='avatar.jpg')获取这个文件的URL,这个函数调用生成的URL为/static/avatar.jpg,在浏览器中输入[http://localhost:5000/static/avatar.jpg](http://localhost:5000/static/avatar.jpg) 即可访问这个图片。在模板watchlist2.html里,我们在用户名的左侧添加了这个图片,使用url_for()函数生成图片src属性所需的图片URL,如下所示: ```
``` 另外,我们还创建了一个存储CSS规则的styles.css文件,我们使用下面的方式在模板中加载这个文件: ```
``` ### 1.添加Favicon 在运行前两章的示例程序时,我们经常在命令行看到一条404状态的GET请求记录,请求的URL为/favicon.ico,如下所示: ``` 127.0.0.1 - - [08/Feb/2018 18:31:12] "GET /favicon.ico HTTP/1.1" 404 - ``` 这个favicon.ico文件指的是Favicon(favorite icon,收藏夹头像/网站头像),又称为shortcut icon、tab icon、website icon或是bookmark icon。顾名思义,这是一个在浏览器标签页、地址栏和书签收藏夹等处显示的小图标,作为网站的特殊标记。浏览器在发起请求时,会自动向根目录请求这个文件,在前面的示例程序中,我们没有提供这个文件,所以才会产生上面的404记录。 要想为Web项目添加Favicon,你要先有一个Favicon文件,并放置到static目录下。它通常是一个宽高相同的ICO格式文件,命名为favicon.ico。 除了ICO格式,PNG和(无动画的)GIF格式也被所有主流浏览器支持。 Flask中静态文件的默认路径为/static/filename,为了正确返回Favicon,我们可以显式地在HTML页面中声明Favicon的路径。首先可以在部分添加一个
元素,然后将rel属性设置为icon,如下所示: ```
``` > 附注 大部分教程将rel属性设置为shortcut icon,事实上,shortcut是多余的,可以省略掉。 ### 2.使用CSS框架 在编写Web程序时,手动编写CSS比较麻烦,更常见的做法是使用CSS框架来为程序添加样式。CSS框架内置了大量可以直接使用的CSS样式类和JavaScript函数,使用它们可以非常快速地让程序页面变得美观和易用,同时我们也可以定义自己的CSS文件来进行补充和调整。以Bootstrap([http://getbootstrap.com/](http://getbootstrap.com/) )为例,我们需要访问Bootstrap的下载页面([http://getbootstrap.com/docs/4.0/getting-started/download/](http://getbootstrap.com/docs/4.0/getting-started/download/) )下载相应的资源文件,然后分类别放到static目录下。 Bootstrap是最流行的开源前端框架之一,它有浏览器支持广泛、响应式设计等特点。使用它可以快速搭建美观、现代的网页。Bootstrap的官方文档([http://getbootstrap.com/docs/](http://getbootstrap.com/docs/) )提供了很多简单易懂的示例代码。 通常情况下,CSS和JavaScript的资源引用会在基模板中定义,具体方式和加载我们自定义的styles.css文件相同: ``` ... {% block styles %}
{% endblock %} ... {% block scripts %} {% endblock %} ... ``` 如果不使用Bootstrap提供的JavaScript功能,那么也可以不加载。另外,Bootstrap所依赖的jQuery([https://jquery.com/](https://jquery.com/) )和Popper.js([https://popper.js.org/](https://popper.js.org/) )需要单独下载,这三个JavaScript文件在引入时要按照jQuery→Popper.js→Boostrap的顺序引入。 虽然我建议在开发时统一管理静态资源,如果你想简化开发过程,那么从CDN加载是更方便的做法。从CND加载时,只需要将相应的URL替换为CDN提供的资源URL,比如: ``` ... {% block styles %}
{% endblock %} ... {% block scripts %} {% endblock %} ... ``` ### 3.使用宏加载静态资源 为了方便加载静态资源,我们可以创建一个专门用于加载静态资源的宏,如代码清单所示。 ``` {% macro static_file(type, filename_or_url, local=True) %} {% if local %} {% set filename_or_url = url_for('static', filename=filename_or_url) %} {% endif %} {% if type == 'css' %}
{% elif type == 'js' %} {% elif type == 'icon' %}
{% endif %} {% endmacro %} ``` 在模板中导入宏后,只需在调用时传入静态资源的类别和文件路径就会获得完整的资源加载语句。使用它加载CSS文件的示例如下: ``` static_file('css', 'css/bootstrap.min.css') ``` 使用它也可以从CDN加载资源,只需要将关键字参数local设为False,然后传入资源的URL即可: ``` static_file('css', 'https://maxcdn.../css/bootstrap.min.css', local=False) ``` # 3、消息闪现 Flask提供了一个非常有用的flash()函数,它可以用来“闪现”需要显示给用户的消息,比如当用户登录成功后显示“欢迎回来!”。在视图函数调用flash()函数,传入消息内容即可“闪现”一条消息。当然,它并不是我们想象的,能够立刻在用户的浏览器弹出一条消息。实际上,使用功能flash()函数发送的消息会存储在session中,我们需要在模板中使用全局函数get_flashed_messages()获取消息并将其显示出来。 通过flash()函数发送的消息会存储在session对象中,所以我们需要为程序设置密钥。可以通过app.secret_key属性或配置变量SECRET_KEY设置,具体可参考之前的相关内容。 你可以在任意视图函数中调用flash()函数发送消息。为了测试消息闪现,我们添加了一个just_flash视图,在函数中发送了一条消息,最后重定向到index视图,如代码清单所示: ``` from flask import Flask, render_template, flash app = Flask(__name__) app.secret_key = 'secret string' @app.route('/flash') def just_flash(): flash('I am flash, who is looking for me?') return redirect(url_for('index')) ``` Flask提供了get_flashed_message()函数用来在模板里获取消息,因为程序的每一个页面都有可能需要显示消息,我们把获取并显示消息的代码放在基模板中content块的上面,这样就可以在页面主体内容的上面显示消息: ```
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} {% block content %}{% endblock %}
``` 因为同一个页面可能包含多条要显示的消息,所以这里使用for循环迭代get_flashed_message()返回的消息列表。另外,我们还为消息定义了一些CSS规则,你可以在示例程序中的static/styles.css文件中查看。现在访问[http://localhost:5000](http://localhost:5000) 打开示例程序的主页,如果你单击页面上的Flash something链接(指向/flash),页面重载后就会显示一条消息,如图3-4所示。  图3-4 消息闪现示例 当get_flashed_message()函数被调用时,session中存储的所有消息都会被移除。如果你这时刷新页面,会发现重载后的页面不再出现这条消息。 # 4、自定义错误页面 当程序返回错误响应时,会渲染一个默认的错误页面,我们在第2章和它们打过招呼。默认的错误页面太简单了,而且和其他页面的风格不符,导致用户看到这样的页面时往往会不知所措。我们可以注册错误处理函数来自定义错误页面。 错误处理函数和视图函数很相似,返回值将会作为响应的主体,因此我们首先要创建错误页面的模板文件。为了和普通模板区分开来,我们在模板文件夹templates里为错误页面创建了一个errors子文件夹,并在其中为最常见的404和500错误创建了模板文件。 404页面模板: ``` {% extends 'base.html' %} {% block title %}404 - Page Not Found{% endblock %} {% block content %}
Page Not Found
You are lost...
{% endblock %} ``` 错误处理函数需要附加app.errorhandler()装饰器,并传入错误状态码作为参数。错误处理函数本身则需要接收异常类作为参数,并在返回值中注明对应的HTTP状态码。当发生错误时,对应的错误处理函数会被调用,它的返回值会作为错误响应的主体。下面代码是用来捕捉404错误的错误处理器: ``` from flask import Flask, render_template ... @app.errorhandler(404) def page_not_found(e): return render_template('errors/404.html'), 404 ``` 错误处理函数接收异常对象作为参数,内置的异常对象提供了下列常用属性,如表3-7所示。  表3-7 Werkzeug内置的HTTP异常类的常用属性 如果你不想手动编写错误页面的内容,可以将这些信息传入错误页面模板,在模板中用它们来构建错误页面。不过需要注意的是,传入500错误处理器的是真正的异常对象,通常不会提供这几个属性,你需要手动编写这些值。 我们在第2章介绍过,Flask通过抛出Werkzeug中定义的HTTP异常类来表示HTTP错误,错误处理函数接收的参数就是对应的异常类。基于这个原理,你也可以使用app.errorhandler()装饰器为其他异常注册处理函数,并返回自定义响应,只需要在app.errorhandler()装饰器中传入对应的异常类即可。比如,使用app.errorhandler(NameError)可以注册处理NameError异常的函数。 这时如果访问一个错误的URL(即未在程序中定义的URL),比如[http://localhost:5000/nothing](http://localhost:5000/nothing) ,将会看到如图3-5所示的错误页面。  图3-5 自定义404错误页面 除了404错误,我们还需要为另一个最常见的500错误编写错误处理器和模块,这些代码基本相同。 # 5、 JavaScript和CSS中的Jinja2 当程序逐渐变大时,很多时候我们会需要在JavaScript和CSS代码中使用Jinja2提供的变量值,甚至是控制语句。比如,通过传入模板的theme_color变量来为页面设置主题色彩,或是根据用户是否登录来决定是否执行某个JavaScript函数。 首先要明白的是,只有使用render_template()传入的模板文件才会被渲染,如果你把Jinja2代码写在单独的JavaScript或是CSS文件中,尽管你在HTML中引入了它们,但它们包含的Jinja2代码永远也不会被执行。对于这类情况,下面有一些Tips: ### 1.行内/嵌入式JavaScript/CSS 如果要在JavaScript和CSS文件中使用Jinja2代码,那么就在HTML中使用` ``` 在CSS文件中,使用var()函数并传入变量名即可获取对应的变量值: ``` #foo { color: var(--theme-color); } #bar { background: var(--background-url); } ```