《Flask Web开发》笔记——第三章 模板

在学习这一章之前,我们先谈以下MVC架构(如下图)。MVC架构指的是业务模型(Model),用户界面(View)和控制器(Controller)。这是进行一种软件设计的典范,在web设计的时候,常常采用这种架构。
看看前面第二章的hello.py,在Flask中一般把其中的hello_world()函数称为视图(view)函数,但是实际上这种称法并不确切。但是这个程序中,hello_world()函数确实返回了响应的内容,就是 return 'hello world'。这种做法在实际web开发的时候却会遇到问题,因为真实呈现给用户的网页往往非常复杂,把后端的处理方式(书中称为业务逻辑)和前端呈现给大家的内容(书中称为表现逻辑)混在一起,导致视图函数(一定要记住,Flask中的视图函数,实际上并非MVC中的V,而是属于MVC中的C)非常臃肿,变得难以维护。
简单地说,用户看不见的部分是业务逻辑。而用户看见的部分是表现逻辑。

《Flask Web开发》笔记——第三章 模板_第1张图片
MVC架构

一个类比:
这里突然想到,进行web开发与组织一场大型晚会很类似,如果80个人计划筹备一场晚会,如果所有人都是幕后工作人员,来指挥如何开展各项工作,所有人也都是演员,都需要上台演出,仓库里的道具大家都可以安排使用。这样在布置舞台,排练节目,分配道具资源的时候,一定会乱成一团。
于是,大家分了一下工,成立了一个以导演为主的管理团队来进行总体负责,另外一部分人成立了演出团队,专门排练节目呈现给观众,还有一部分人负责各项资源的统一管理。这样就形成了一个MVC架构,M就是资源管理团队,管理道具仓库和制定仓库资源调配规则,V就是演员团队,负责把节目呈现给观众,C就是管理团队,根据观众的喜好和反馈安排节目。当然,这个类比并不绝对,因为晚会安排好之后,和观众的互动与web不太一样,但大体上还是很类似的。

业务逻辑与表现逻辑

那么,实际的Flask中,需要将业务逻辑和表现逻辑进行分离,而呈现给用户端的部分,采用模板来实现。
模板是一个包含响应文本的文件, 其中包含用占位变量表示的动态部分,其具体值只在请求的上下文中才能知道。 使用真实值替换变量,再返回最终得到的响应字符串,这一过程称为渲染(render)。为了渲染模板, Flask 使用了一个名为 Jinja2 的强大模板引擎。

1. Jinja2模板引擎

模板才是MVC架构中真正的View。所谓模板,就是专门负责浏览器能够理解的HTML部分。
那么,你可能会问,我们做好一个模板放在那里调用就可以了,这个Jinga2,这个“神社[じんじゃ]”,这个模板引擎到底是干什么的呢?


《Flask Web开发》笔记——第三章 模板_第2张图片
模板到最终HTML流程

从上图中看到,模板引擎就模板文件和数据结合在一起,生成了最终的HTML文档。由此我们可以总结如下:

  • 模板文件并不是直接的HTML文档,而只是HTML文档的一个框架,其中也包含了一些地方需要你去添加。
  • 但是模板应该包含了你需要反复用到的大部分HTML内容,代码重用是模板的一个主要目的,不然也不能称之为模板了。
  • 模板中需要添加的地方,是你需要利用控制器,也就是Flask中的视图函数进行赋值。
  • 对模板的赋值,有时候需要依赖一些逻辑,不然模板的功能就会受到极大的限制,因此需要对模板内的一些逻辑语句进行处理
  • 根据上面四点需求,模板引擎的主要功能:一是提供渲染模板的规则,二是生成最终的HTML文档。

我想说:模板引擎不生产模板,只是模板的加工匠。
我还想说:模板引擎是模板文件及数据与HTML文档实现之间的一座桥梁。

而在Flask中集成的模板引擎就是jinja2模板引擎,而满足jinja2模板引擎要求的模板就是jinja2模板。

2. 模板的渲染方法

不知道为什么,说到渲染(render)这个词语,总感觉特别高大上。但其实通俗一点的比喻:模板就是一个毛坯房,房型格局都定下来了。渲染就是装修,你可以根据需求进行装修,放置家具,最终可以入住。
那这个装修工就是Flask的视图函数。Flask提供的render_template函数把Jinja2模板引擎集成到程序中。以如下的代码来示例模板的渲染:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('\')
def index():
    return render_template('index.html')

@app.route('\use\')
def user(name):
return render_template('user.html', name=name)

而在这里,视图函数index()中,通过render_template函数引入了模板index.html, 而视图函数user(name)通过render_template函数引入了模板user.html, 并将值赋给了模板中的变量name。这里,render_templates函数的第一个参数是模板的文件名,后面的参数都应该是键值对,表示模板中变量对应的真实值。
此处,这两个模板分别为如下:

  • templates/index.html

Hello World!

  • templates/user.html

Hello, {{ name }}!

这里我们就来看看模板是如何渲染的。很明显,视图函数index()直接返回了index.html文件,并没有对模板作任何改动。
而视图函数user(name)对模板给出了一个数字,就是参数name,这个参数可以赋值给模板user.html中的某个变量。我们接着从render_template函数的第二个参数name=name可以知道,user.html中存在一个变量名name,视图函数的输入值被赋给了这个变量。我们看看user.html中的这个变量吧。它的形式是{{ name }},在Jinja2模板中,这种{{ name }}结构表示一个变量,它是一种特殊的占位符,它告诉模板引擎从模板的这个位置插入渲染模板时使用的数据。
Jinja2能识别所有类型的变量,包括列表、字典和对象。示例如下:

A value from a dictionary: {{ mydict['key'] }}.

A value from a list: {{ mylist[3] }}.

A value from a list, with a variable index: {{ mylist[myintvar] }}.

A value from an object's method: {{ myobj.somemethod() }}.

也可以使用过滤器修改变量,如下为使用过滤器的示例:

Hello, {{ name|capitalize }}

Jinja2提供部分常用的过滤器如下表所列。


《Flask Web开发》笔记——第三章 模板_第3张图片
Jinja2变量过滤器

除了上面所用到的直接对模板占位符处进行赋值,Jinja2还提供了多种控制流程,用来改变模板的渲染过程。这些控制结构包括:

  • 条件控制语句,其可以根据视图函数的赋值而返回不同的内容
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
  • 循环控制语句,可以在模板中渲染一组元素。
    {% for comment in comments %}
  • {{ comment }}
  • {% endfor %}
  • 宏(macro),类似于Python代码中的函数。
{% macro render_comment(comment) %}
  • {{ comment }}
  • {% endmacro %}
      {% for comment in comments %} {{ render_comment(comment) }} {% endfor %}

    这里解释一下:在上面代码的前三行中,定义了一个宏。这个宏可以看作是python的函数render_comment(comment),其作用是在模板中插入comment的值。后面五行中,完成了对这个宏的调用。
    另外一点是,macro可能会反复使用到,可以将其保存在单独的文件中,然后在需要的时候导入到模板中。假如macro被保存在macros.html这个文件中,而在导入的时候,需要用到的语句是{% import 'macros.html' as macros %}

    • 模板代码片段复用:对于需要多次重复使用的模板代码片,可以单独保存在一个文件中,再包含在所有的模板中,可以避免重复。例如有一个common.html的文件,包含了这些重复利用的代码片,在模板中可以通过{% include 'common.html' %}包括进去。
    • 模板继承:类似于python中类的继承。对于一个基类模板,例如base.html,如果在新的模板中需要继承这个基类模板,使用到的语句是{% extends "base.html" %}

    现在对Jinja2模板引擎的控制结构作一下小结如下图:


    《Flask Web开发》笔记——第三章 模板_第4张图片
    Jinja2控制结构

    3. 使用Flask-Bootstrap集成Twitter Bootstrap

    Bootstrap是Twitter开发的一个开源框架。如下图LOGO下面的描述:简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。因此,这是一个前端开发框架,不会涉及到服务器。

    Bootstrap

    而这样一个开源框架,提供了前端所用到的一些层叠样式(CSS)和JavaScript文件。而Flask-Bootstrap这个Flask扩展,可以简化我们将Bootstrap集成到程序这个过程。
    一个注意的地方,前面也提到过,在示例3-4中,提到的这种导入Flask-Bootstrap扩展的方式是错误的,在python3中,不采用 from flask.ext.bootstrap import Bootstrap这个语句来导入,而是采用如下方式:

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

    Flask-Bootstrap的使用方式参考书中的方式应该很好理解,这里就不在赘述。

    4. 定义错误页面

    常见的错误代码有如下这两个,可以分别自定义错误页面,在对应错误发生时显示该页面。

    • 404: 客户端请求未知页面或路由时显示
    • 500:有未处理的异常时显示
      对于错误页面,继承基模板来显示错误信息,界面更美观。

    5. 链接

    url_for()函数可以使用程序URL映射中保存的信息生成URL。其最简单的用法是使用视图函数名为参数,返回对应的URL。
    例如:url_for('index'),会返回视图函数index()所对应的URL。
    url_for()函数在生成动态地址时,动态部分可以作为关键字参数传入。例如url_for ('user', name='john', _external=True)这种形式。
    传入url_for()的关键字参数不仅限于动态路由中的参数。函数能将任何额外参数添加到查询字符串中。例如,url_for('index', page=2) 的返回结果是/?page=2

    6. 静态文件

    静态文件:包括网页使用到的图片,JavaScript源码文件和CSS。
    默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件。

    你可能感兴趣的:(《Flask Web开发》笔记——第三章 模板)