视图函数的作用是生成请求的响应。很多情况下,请求会改变应用的状态,而这种变化就发生在视图函数中。

以用户在网站中注册新账户的过程为例。用户在表单中输入电子邮件地址和密码,然后提交。服务器接收到包含用户输入数据的请求,然后Flask把请求分派给处理注册请求的视图函数。这个视图函数访问数据库,添加新用户,生成响应回送浏览器,指明操作成功/失败。这两个过程分别为业务逻辑和表现逻辑。

视图函数里包含函数和HTML代码,但这两部分在一起会影响理解和维护,所有用模板来保存HTML代码。

模板是包含响应的文本文件,其中包含用占位变量表示的动态部分,其具体值只在请求上下文中才能知道。使用真实值替换变量,再返回最终得到的响应字符串,这一过程称为渲染。Flask使用Jinja2的模板引擎。

一、Jinja2模板引擎
例1 templates/index.html:Jinja2模板

<h1>Hello World!</h1>

例2 templates/user.html:Jinja2moban

<h1>Hello, {{ name }}!</h1>

(一)、渲染模板
在Flask根目录中新建子目录,将上面的两个模板(html)文件保存在里面。

这块网上的教程也没清楚的说在哪建,看了好几遍书里的那段句话“Flask在应用目录中的templates子目录里寻找模板。在下一个hello.py版本中,你要新建templates子目录,再把前面定义的模板保存在里面,分别命名为index.html和user.html。”我才明白是在根目录下创建子目录templates,而不是在 文件-设置-编辑器-文件和代码模块 里创建模板。并且吧,templates子目录创建完后,我一直没法选中它,创建的两个html文件全在.idea文件夹里,我还要剪切过来,emmmmm希望一边学一边能弄清楚吧。

例3 hello.py:渲染模板

from flask import Flask, render_templateapp = Flask(__name__)@app.route('/')
def index():return render_template('index.html')@app.route('/user/<name>')
def user(name):return render_template('user.html', name=name)

Flask提供的render_template()函数把Jinja2模板引擎集成到了应用中。这个函数的第一个参数名是模板的文件名,随后的参数都是键-值对,表示模板中变量对应的具体值。
(如这段代码中,第二个模板收到一个名为name的变量)

name=name是经常使用的关键字参数。左边的name表示参数名,即模板中的占位符;右边的name是当前作用域中的变量,表示同名参数的值。

讲道理,以前我总以为是书不对,书不贴近实操,还得靠别人的经验教程,但学到现在我也算明白了,这一系列动物书,里面都是精华内容,吃透了绝对有很大的提高,而网上的教程只是辅助,只是更灵活,但核心知识还是在书上的,怎么说吧,两边都要看,但书还是更重要一些吧。
我的三大错觉:书写错了,编译器有问题,标准库不对。

(二)、变量
{{ name }}结构表示一个变量,是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取。

Jinja2能识别所有类型的变量,甚至是一些复杂的类型,如列表、字典和对象。

变量的值可以通过过滤器修改,过滤器添加在变量名之后,二者之间以竖线分隔。

例,下述模板把name变量的值变成首字母大写的形式:
Hello, {{ name|capitalize}}

Jinja2变量过滤器表
过滤器名 说明
safe 渲染值时不转义
capitalize 把值的首字母转换成大写,其他转换成小写
lower 把值转换成小写
upper 把值转换成大写
title 把值中每个单词的首字母都转换成大写
trim 把值的首尾空格删掉
striptags 渲染之前把值中所有的HTML标签都删掉

默认情况下,出于安全考虑,Jinja2会转义所有变量。例如,如果一个变量的值为<h1>Hello</h1>,Jinja2会将其渲染成’<h1>Hello</h1&gt’,浏览器能显示这个h1元素,但不会解释它。
很多情况下需要显示变量中存储的HTML代码,这时就可以使用safe过滤器。

一定不能在不可信的值上使用safe过滤器,例如用户在表单中输入的文本。

(三)、控制结构
Jinja2提供了多种控制结构,可用来改变模板的渲染流程。

例,如何在模板中使用条件判断语句:

{% if user %}Hello, {{ user }}!
{% else %}Hello, Stranger!
{% endif %}

例,在模板中渲染一组元素,使用for循环实现需求:

<ul>{% for comment in comments %}<li>{{ comment }}</li>{% endfor %}
</ul>

Jinja2还支持宏。宏类似于Python代码中的函数:

{% macro render_comment(comment) %}<li>{{ comment }}</li>
{% endmacro %}<ul>{% for comment in comments %}{{ render_comment(comment) }}{% endfor %}
</ul>

为了重复使用宏,可以把宏保存在单独的文件中,然后在需要使用的模板中导入:

{% import 'macros.html' as macros %}
<ul>{% for comment in comments %}{{ macros.render_comment(comment) }}{% endfor %}
</ul>

需要在多处重复使用的模板代码片段可以写入单独的文件,再引入所有模板中,以避免重复:

{% include 'common.html' %}

另一种重复使用代码的方式是模板继承,这类似于Python中的类继承。

首先,创建一个名为base.html的基模板:

<html>
<head>{% block head %}<title>{% block title %}{% endblock %} - My Application</title>{% endblock %}
</head>
<body>{% block body %}<% endblock %}
</body>
<html>

基模板中定义的区块可在衍生模板中覆盖。Jinja2使用block和endblock指令在基模板中定义内容区块。
在本例中,我们定义了名为head、title和body的区块(title包含在head中)。

下例是基模板的衍生模板:

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}{{ super() }}<style></style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}}

extends指令声明这个模板衍生自base.html。
如果基模板和衍生模板中的同名区块中都有内容,衍生模板中的内容将显示出来。在衍生模板的区块里可以调用super(),引用基模板中同名区块里的内容(如上例中的head)。

二、使用Flask-Bootstrap集成Bootstrap
Bootstrap是Twitter开发的一个开源Web框架,它提供的用户界面组件可用于创建整洁且具有吸引力的网页,而且兼容所有现代的桌面和移动平台Web浏览器。

Bootstrap是客户端框架,因此不会涉及服务器。服务器需要做的只是提供引用了Bootstrap层叠样式表(CSS,cascading style sheet)和JavaScript文件的HTML响应,并在HTML、CSS和JavaScript代码中实例化所需的用户界面元素。

这些操作最理想的执行场所就是模板。

要想在应用中集成BootStrap,最直接的方法是根据BootStrap文档中的说明对HTML模板进行必要的改动。但这个任务使用Flask扩展处理要简单得多,相关得改动不会导致主逻辑混乱不堪。

安装扩展Flask-BootStrap:

(venv) $ pip install flask-bootstrap

怎么硕呢,Flask有关得第三方库一般都推荐在虚拟环境下安装,但是把,cmd安装这些第三方库实在是太麻烦了,最开始搭环境就搭了两三天,后来我干脆直接全局安装第三方库了。后来听老师的用了pycharm…简单了太多,所以直接在pycharm里对应的虚拟环境下安装Flask-BootStrap吧。

例 hello.py:初始化Flask-BootStrap

from flask_bootstrap import Bootstrap
#...
bootstrap = Bootstrap(app)

扩展通常从flask_包中导入,其中是扩展的名称。多数Flask扩展采用两种初始化方式中的一种。

在上例中,初始化扩展的方式是把应用实例作为参数传给构造函数。

初始化Flask-BootStrap之后,就可以在应用中使用一个包含所有BootStrap文件和一般结构的基模板。应用利用Jinja2的模板继承机制来扩展这个基模板。
下例是把user.html改写为衍生模板后的新版本。

例 templates/user.html:使用Flask-BootStrap的模板

{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>
</button><a class="navbar-brand" href="/">Flasky</a></div><div class="navbar-collapse collapse"><ul class="nav navbar-nav"><li><a href="/">Home</a></li></ul></div></div>
</div>
{% endblock %}
{% block content %} <div class="container"><div class="page-header"><h1>Hello, {{ name }}!</h1></div></div>
{% endblock %}

效果如下图所示:

Jinja2中的extends指令从Flask-BootStrap中导入bootstrap/base.html,从而实现继承。Flask-BootStrap的基模板提供了一个网页骨架,引入了BootStrap的所有CSS和JavaScript文件。

上面的user.html模板定义了3个区块,分别为title、navbar和content。这些区块都是基模板提供的,可在衍生模板中重新定义。title区块的内容会出现在渲染后的HTML文档头部,放在标签中。navbar和content这两个区块分别表示页面中的导航栏和主体内容。

在这个模板中,navbar区块使用BootStrap组件定义了一个简单的导航栏。content区块中有个

容器,其中包含一个页头。改动之后如图所示:

成功后的图。

Flask-Bootstrap基模板中定义的区块
区块名 说明
doc 整个HTML文档
html_attribs 标签中的属性
html 标签中的内容
head 标签中的内容
title 标签中的内容<br/> metas 一组标签<br/> styles CSS声明<br/> body_attribs 标签的属性<br/> body 标签中的内容

navbar 用户定义的导航栏
content 用户定义的页面内容
scripts 文档底部的JavaScripts声明
表中很多区块都Flask-Bootstrap自用的,如果直接覆盖可能会导致一些问题。

例如,Bootstrap的CSS和JavaScript文件在styles和scripts区块中声明。如果应用需要向已经有内容的块中添加新内容,必须使用Jinja2提供的super()函数。

例如,如果要在衍生模板中添加新的JavaScripts文件,需要这么定义scripts区块:

{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}

首先,.py文件里不能忘记加from flask_bootstrap import Bootstrap和bootstrap = Bootstrap(app)两行代码,并且要加到对应的地方去,不然会报错。那串巨长的代码直接覆盖原user.html就行。

三、自定义错误页面
如果你在浏览器地址栏中输入了无效的路由,会看到一个状态码为404的错误页面。

Flask允许应用使用模板自定义错误页面。最常见的错误代码有两个:404,客户端请求未知页面或路由时显示;500,应用有未处理的异常时显示。

使用app.errorhandler装饰器为这两个错误提供自定义的处理函数。
例 hello.py:自定义错误页面

@app.errorhandler(404)
def page_not_found(e):return render_template('404.html'), 404@app.errorhandler(500)
def internal_server_error(e):return render_template('500.html'), 500

与视图函数一样,错误处理函数也返回一个响应。此外,错误处理函数还要返回与错误对应的数字状态码。状态码可以直接通过第二个返回值指定。

错误处理函数中引用的模板也需要我们编写。这些模板应该和常规页面使用相同的布局,因此要有一个导航栏和显示错误消息的页头。

编写这些模板最直接的方法是复制templates/user.html,分别创建templates/404.html和templates/500.html,然后把这两个文件中的页头元素改为相应的错误下消息。但这样很麻烦。

Jinja2的模板继承机制!Flask-Bootstrap提供了一个具有页面基本布局的基模板,同样,应用也可以定义一个具有统一页面布局的基模板,其中包含导航栏,而页面内容则留给衍生模板定义。

下例展示了templates/base.html的内容,这是一个继承自bootstrap/base.html的新模板,其中定义了导航栏。这个模板本身也可以作为其他模板的二级基模板,例如templates/user.html、templates/404.html和templates/500.html。

例 templates/base.html:包含导航栏的应用基模板

{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>
</button><a class="navbar-brand" href="/">Flasky</a></div><div class="navbar-collapse collapse"><ul class="nav navbar-nav"><li><a href="/">Home</a></li></ul></div></div>
</div>
{% endblock %}{% block content %}
<div class="container">{% block page_content %}{% endblock %}
</div>
{% endblock %}

这个模板中的content区块只有一个

容器,其中一个包含一个新的空区块,名为page_content,区块中的内容由衍生模板定义。

现在,应用中的模板继承自这个模板,而不直接继承自Flask-Bootstrap的基模板。通过继承templates/base.html模板编写自定义的404错误页面就简单了。

例 templates/404.html:使用模板继承机制自定义404错误页面

{% extends "base.html" %}{% block title %}Flasky - Page Not Found{% endblock %}{% block page_content %}
<div class="page-header"><h1>Not Found</h1>
</div>
{% endblock %}

效果如下图所示;

templates/user.html模板也可以通过继承这个基模板来简化内容。
例 templates/user.html:使用模板继承机制简化页面模板:

{% extends "base.html" %}{% block title %}Flasky{% endblock %}{% block page_content %}
<div class="page-header"><h1>Hello, {{ name }}!</h1>
</div>
{% endblock %}

模板继承extends,html的知识忘记了好些,看代码是能看懂,背不下来啊。。。Bootstrap的语法有点难背嗷。。。

四、链接
任何具有多个路由的应用都需要可以连接不同页面的链接,例如导航栏。

在模板中直接编写简单路由的URL链接不难,但对于包含可变部分的动态路由,在模板中构建正确的URL就很难了。而且直接编写URL会对代码中定义的路由产生不必要的依赖关系。如果重新定义路由,模板中的链接可能会失效。

url_for()辅助函数,它使用应用的URL映射保存的信息生成URL。

url_for()函数最简单的用法是以视图函数名(或者app.add_url_route()定义路由时使用的端点名)作为参数,返回对应的URL。
例如,在当前版本的hello.py应用中调用url_for(‘index’)得到的结果是/,即应用的根URL。
调用url_for(‘index’, _external=True)返回的则是绝对地址,在这个示例中是http://localhost:5000/。

生成连接应用内不同路由的链接时,使用相对地址就足够了。如果要生成在浏览器之外使用的链接,则必须使用绝对地址,例如电子邮件中发送的链接。

使用url_for()生成动态URL时,将动态部分作为关键字参数传入。例如,url_for(‘user’, name=‘json’, _external=True)的返回结果是http://localhost:5000/user/john。

传给url_for()的关键字参数不仅限于动态路由中的参数,非动态的参数也会添加到查询字符串中。
例如,url_for(‘user’, name=‘john’, page=2, version=1)的返回结果是/user/john?page=2&version=1。

五、静态文件
Web应用不是仅由Python代码和模块组成。多数应用还会使用静态文件,例如模板中HTML代码引用图像、JavaScript源码文件和CSS。

在审查hello.py应用的URL映射,其中有一个static路由。这是Flask为了支持静态文件而自动添加的,这个特殊路由的URL是/static/。例如,调用url_for(‘static’, filename='css/style.css, _external=True)得到的结果是http://loacalhost:5000/static/css/styles.css。

默认设置下,Flask在应用根目录中名为static的子目录中寻找静态文件。如果需要,可在static文件夹中使用子文件夹存放文件。服务器收到映射到static路由上的URL后,生成的响应包含文件系统中对应文件里的内容。

例 templates/base.html:定义收藏夹图标
{% block head %}
{{ super() }}

{% endblock %}

这个图标的声明插入head区块的末尾。注意,为了保留基模板中这个区块里的原始内容,我们调用了super()。

六、使用Flask-Moment本地化日期和时间
服务器需要统一时间单位,这和用户所在的地理位置无关,所以一般使用协调世界时(UTC,coordinad universal time)。不过用户看到UTC格式的时间会感到困惑,他们更希望看到当地时间,而且采用当地惯用的格式。

要想在服务器上只使用UTC时间,一个优雅的解决方案是,把时间单位发送给Web浏览器,转换成当地时间,然后用JavaScript渲染。Web浏览器能更好地完成这一任务,因为它能获取用户计算机中的时间和区域设置。

有一个使用JavaScript开发的优秀客户端开源库,名为Moment.js,它可以在浏览器中渲染日期和时间。Flask-Moment是一个Flask扩展,能简化把Moment.js集成到Jinja2模板中的过程。
Flask-Moment安装:

(venv) $ pip install flask-moment

例 hello.py:初始化Flask-Moment

from flask_moment import Moment
moment = Moment(app)

除了Moment.js,Flask-Moment还依赖jQuery.js。要在HTML文档的某个地方引入这两个库,可以直接引入,这样可以选择使用哪个版本,也可使用扩展提供的辅助函数,从内容 分发网络(Content Delivery Network,CDN)中引入通过测试的版本。Bootstrap已经引入了 jQuery.js,因此只需引入Moment.js即可。

例 templates/base.html:引入Moments.js库

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

这个区块放在templates/base.html的最后。

为了处理时间戳,Flask-Moment向模板开放了moment对象。

例 hello.py:添加一个datetime变量

from datetime import datetime@app.route('/')
def index():return render_template('index.html',current_time=datetime.utcnow())

把变量current_time传入模板进行渲染。

例 templates/index.html:使用Flask-Moment变量

<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>

展示了如何渲染模板变量current_time。

format(‘LLL’)函数根据客户端计算机中的时区和区域设置渲染日期和时间。参数决定了渲染的方式,从’L’到’LLL’分贝对应不同的复杂度。format()函数还可以接受很多自定义的格式说明符。

我一直以为是我代码打错了。。。原来是def index()部分代码重复了,改完忘删了。。。

但是这里一直有一个错误,暂时存疑吧,详见:
https://blog.csdn.net/csdnjava2017/article/details/78168861

Flask Web开发——(二)模板相关推荐

  1. Flask Web开发基础实战-1.0用户认证与注册模块

    目录: 前言: 一,账户密码安全性 使用Werkzeug实现密码散列 二,创建登录的认证蓝本 三,Flask-Login认证用户 1.用于登录的用户数据库模型 2.保护路由 3.添加登录表单 4.登入 ...

  2. flask web开发是前端还是后端_Flask Web开发实战:入门、进阶与原理解析 PDF 全格式版...

    给大家带来的一篇关于Flask相关的电子书资源,介绍了关于Flask.Web.开发实战方面的内容,本书是由机械工业出版社出版,格式为PDF,资源大小12.2M,李辉编写,目前豆瓣.亚马逊.当当.京东等 ...

  3. 《Flask Web开发:基于Python的Web应用开发实战》笔记(原创)

    内容提要 在学习"狗书"<Flask Web开发:基于Python的Web应用开发实战>的过程中,一直遇到各种各样的坑.该书的第一部分是"Flask简介&qu ...

  4. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(上)

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构 前言 学习Python也有一个半月时间了,学到现在感觉还是 ...

  5. Flask Web开发:基于Python的Web应用开发实战

    <Flask Web开发:基于Python的Web应用开发实战> 虽然简单的网站(Flask+Python+SAE)已经上线,但只是入门.开发大型网站,系统地学习一遍还是有必要的. 201 ...

  6. 《Flask Web开发实战:入门、进阶与原理解析》读书笔记

    写在前面 学docker编排,有一个用Flask框架的Demo,感觉挺方便,所以学习下 基于<Flask Web开发实战:入门.进阶与原理解析>做的读书笔记 个人还是比较喜欢看书,看书的话 ...

  7. 学习《Flask Web开发:基于Python的Web应用开发实战》分享

    学习<Flask Web开发:基于Python的Web应用开发实战>分享一直在说学习Python,对同事,对朋友,都说我正在学习Python,这无形给自己一定的压力,促使自己要去学习,进步 ...

  8. Flask Web开发入门

    Flask Web开发入门(八)之文件上传 https://blog.csdn.net/kangkanglou/article/details/79027425 前端:详情见上面的链接/也可以直接用f ...

  9. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(下)

    目录 前言 第8章 用户认证 第9章 用户角色 第10章 用户资料 第11章 博客文章 第12章 关注者 第13章 用户评论 第14章 应用编程接口   前言 第1章-第7章学习实践记录请参见:< ...

  10. Flask Web开发入门(十一)之图片展现

    在Flask Web开发入门(十)之图片上传(使用Flask-Uploads)我们介绍了使用Flask插件Flask-Uploads上传图片,本章就此话题继续展开. 我们知道,我们可以通过UPLOAD ...

最新文章

  1. Android 自定义ProgressBar 实现进度圆环
  2. _catboost.CatBoostError: C:/Program Files (x86)/Go Agent/pipelines/BuildMaster/catboost.git/catboost
  3. UITesting Bundle使用
  4. 13:TextView#setText、DalvikART虚拟机
  5. Android短消息备份闪退,Android Socket发送信息时闪退
  6. keras、tf、numpy实现logloss对比
  7. jira使用教程pdf_需要申请项目?需要开通权限?需要创建流程?刚需教程安排上了!...
  8. 带圈汉字 在线生成_手写签名在线生成器-手写签名在线生成器可复制
  9. django mysql 创建表_关于 django ORM 中,数据库建表方式的问题
  10. 机器学习算法总结之Bagging与随机森林
  11. 【渝粤教育】国家开放大学2019年春季 0390-22T古代诗歌散文专题 参考试题
  12. 课堂随笔01--进制转换
  13. 小蠓虫如何灭_小咬怎么灭
  14. 牛客网——复杂字符串排序
  15. Think PHP(TP)框架基础知识
  16. 锐捷客户端 开机不能自动联网 无法启动DHCP Client服务
  17. 阿里云解决方案汇总,24种上云场景,20O+篇企业上云实践
  18. 03-数据解析_xpath(04 【实战】豆瓣电影、电影天堂爬虫)
  19. 网站横幅切换jquery 插件
  20. python 回归方程及回归系数的显著性检验_使用Excel和python来做回归分析

热门文章

  1. Post-gress-Q-L12安装
  2. ubuntu20.04配置samba服务器
  3. “羊了个羊”Java版本实现
  4. java创建文件夹和文件并写入
  5. JetBraains Toolbox 学生授权 (IntelliJ IDEA 学生授权登录)
  6. 02_MySQL环境搭建
  7. python 设置全局代理_Python中使用socks5设置全局代理的方法示例
  8. 2B2T服务器的现状
  9. 【安全】Netgear R7000/R6400等多款路由器曝远程任意命令注入
  10. 华为新系统鸿蒙手机测试,首个华为鸿蒙2.0测试 电池续航提升将近10%