Flask Web开发——(二)模板

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

以用户在网站中注册新账户的过程为例。用户在表单中输入电子邮件地址和密码,然后提交。服务器接收到包含用户输入数据的请求,然后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_template

app = 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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值