Flask 蓝图的基本使用

在前面的例子中,所有的页面处理逻辑都是放在同一个文件中,随着业务代码的增加,将所有代码都放在单个程序文件中是非常不合适的。不仅会让阅读代码变得困难,而且会给后期维护带来麻烦。

Flask 中使用蓝图,提供了模块化管理程序路由的功能,使程序结构更加清晰。

1. 蓝图简介

随着 Flask 程序越来越复杂,需要对程序进行模块化的处理,蓝图 (Blueprint) 是 Flask 程序的模块化处理机制,它是一个存储视图方法的集合,Flask 程序通过 Blueprint 来组织 URL 以及处理请求。

Flask 的 Blueprint 具有如下属性:

  • 一个项目可以具有多个 Blueprint;
  • Blueprint 可以单独具有自己的模板、静态文件的目录;
  • 在应用初始化时,注册需要使用的 Blueprint。

2. 基本用法

2.1 功能概述

本节通过一个具体的例子讲解 Blueprint 的基本用法,假设网站包含有如下 4 个页面:

页面 功能 处理函数
/news/society/ 社会新闻版块 society_news
/news/tech/ IT 新闻版块 tech_news
/products/car/ 汽车产品版块 car_products
/products/baby/ 婴幼儿产品版块 baby_products

程序将 /news/society/ 和 /news/tech/ 的相关功能组成一个蓝图 news;程序将 /products/car/ 和 /products/baby/ 的相关功能组成一个蓝图 products。

程序中包含 4 个处理函数,根据页面路径,Flask 将请求转发给对应的处理函数。从浏览器发送过来的请求的处理过程如下图所示:

图片描述

假设访问的页面路径是 /products/car,Flask 框架在蓝图 news 和蓝图 products 中查找匹配该页面路径的路由,发现在蓝图 products 中,存在和路径 /products/car 相应的处理函数 car_products,最后将请求转发给函数 car_products 处理。

例子程序包括 2 个蓝图,由 3 个文件构成:

  • app.py,程序的主文件;
  • news.py,实现蓝图 news;
  • products.py,实现蓝图 products。

2.2 主程序 app.py

首先编写主程序 app.py

from flask import Flask
import news
import products

app = Flask(__name__)

app.register_blueprint(news.blueprint)
app.register_blueprint(products.blueprint)

app.run(debug = True)

在第 2 行,导入模块 news.py,在 news.py 中定义了一个蓝图对象 news.blueprint;在第 3 行,导入模块 products.py,在 products.py 中定义了一个蓝图对象 products.blueprint。

在第 7 行,在应用中注册蓝图对象 news.blueprint;在第 8 行,在应用中注册蓝图对象 products.blueprint。

2.3 蓝图 news.py

编写 news.py,实现页面 /news/society 和 /news/tech 的功能:

from flask import Blueprint

blueprint = Blueprint('news', __name__, url_prefix='/news')

@blueprint.route("/society/")
def society_news():
    return "社会新闻版块"

@blueprint.route("/tech/")
def tech_news():
    return "IT 新闻板块"

在第 3 行,创建一个蓝图对象,它包含 3 个参数:

  • 第 1 个参数 ‘news’ 是蓝图的名称;
  • 第 2 个参数 __name__ 是该蓝图所在的模块名,该蓝图的实现文件是 news.py,因此 __name__ 是 ‘news’;
  • 第 3 个参数是指定页面的 URL 前缀为 ‘/news’,它会影响路由中路径的设置,请看下一段的注意。

在第 5 行,将路径 /society/ 和函数 society_news 关联;在第 9 行,将路径 /tech/ 和函数 tech_news 关联。注意:页面的绝对路径是 /news/society/ 和 /news/tech/,因为蓝图的 url_prefix 设置为 news,在蓝图内部,页面的相对路径是 /society/ 和 /tech/。

2.4 蓝图 products.py

编写 products.py,实现页面 /products/car 和 /products/baby 的功能:

from flask import Blueprint

blueprint = Blueprint('products', __name__, url_prefix='/products')

@blueprint.route("/car")
def car_products():
    return "汽车产品版块"

@blueprint.route("/baby")
def baby_products():
    return "婴儿产品版块"    

在第 3 行,创建一个名为 ‘products’ 的蓝图,该蓝图中页面的 URL 前缀为 /products;在第 5 行,将路径 /car/ 和函数 car_products 关联;在第 9 行,将路径 /baby/ 和函数 baby_products 关联。

注意:页面的绝对路径是 /products/car/ 和 /product/baby/,因为蓝图的 url_prefix 等于 products,在蓝图内部,页面的相对路径是 /car/ 和 /baby/。

2.5 运行程序

在浏览器中,访问 http://localhost:5000/news/society/, 显示如下:

图片描述

2.6 源代码下载

3. 更具扩展性的架构

3.1 概述

随着业务代码的增加,需要为 Flask 程序提供一个具备扩展性的架构,根据 Flask 程序的扩展性分为如下三种类型:

1. 所有的页面逻辑放在同一个文件中

在这种架构中,程序完全不具备扩展性。在初学 Flask 时,使用的例子都是这种类型。

2. 使用一个独立的 Python 文件实现蓝图

在这种架构中,程序具备一定的扩展性:

  • 程序由主程序和多个蓝图构成;
  • 每个蓝图对应一个 Python 文件;
  • 所有的蓝图共享相同的模板文件目录;
  • 所有的蓝图共享相同的静态文件目录。

第 2 小节的程序采用的就是这种架构,程序包含 2 个蓝图: news 和 products,由 3 个文件构成:app.pynews.pyproducts.py,其中 news.py 实现新闻版块,products.py 实现产品版块。

3. 使用一个独立的目录实现蓝图

在这种架构中,程序的扩展性最好:

  • 程序由主程序和多个蓝图构成;
  • 每个蓝图对应一个独立的目录,存储与这个蓝图相关的文件;
  • 每个蓝图有一个独立的模板文件目录;
  • 每个蓝图有一个独立的静态文件目录。

3.2 模板文件寻找规则

每个蓝图可以拥有独立的模板文件目录,模板文件寻找规则如下:

  • 如果项目中的 templates 文件夹中存在相应的模板文件,则使用 templates 文件夹下的模板文件;
  • 如果项目中的 templates 文件夹中没有相应的模板文件,则使用定义蓝图的时候指定的 templates 文件夹下的模板文件。

3.3 静态文件寻找规则

每个蓝图可以独立的静态文件目录,静态文件寻找规则如下:

  • 如果项目中的 static 文件夹中存在相应的静态文件,则使用 static 文件夹下的静态文件;
  • 如果项目中的 static 文件夹中没有相应的静态文件,则使用定义蓝图的时候指定的 static 文件夹下的静态文件。

4. 具有扩展性的架构的例子

4.1 目录结构

本节通过实例讲解如何规划目录、使得程序具有良好的扩展性。程序的功能与第 2 小节相同,包含 2 个蓝图:news 和 products,程序的目录结构如下:

图片描述

项目的目录的功能描述如下:

路径 功能描述
templates 项目缺省的模板文件夹
static 项目缺省的静态文件夹
news 蓝图 news 的相关文件
news/templates 蓝图 news 的私有模板文件夹
news/static 蓝图 news 的私有静态文件夹
products 蓝图 products 的相关文件
products/templates 蓝图 products 的私有模板文件夹
products/static 蓝图 products 的私有静态文件夹

项目的文件的功能描述如下:

路径 功能描述
app.py 主程序
news/__init.py__ 蓝图 news 的实现
news/templates/society.html 属于蓝图 news 的一个模板文件
news/static/news.css 属于蓝图 news 的一个静态文件
products/__init.py__ 蓝图 products 的实现

4.2 实现 app.py

首先实现主程序 app.py

from flask import Flask
import news
import products

app = Flask(__name__)

app.register_blueprint(news.blueprint)
app.register_blueprint(products.blueprint)

app.run(debug = True)

在第 2 行,导入模块 news.py,在 news.py 中定义了一个蓝图对象 news.blueprint;在第 3 行,导入模块 products.py,在 products.py 中定义了一个蓝图对象 products.blueprint。

在第 7 行,在应用中注册蓝图对象 news.blueprint;在第 8 行,在应用中注册蓝图对象 products.blueprint。

4.3 实现 news/__init.py__

和蓝图 news 相关的所有文件均放置在项目的 news 目录下,在主程序中导入蓝图 news.py 时,会执行 news/__init.py__:

from flask import Blueprint, render_template

blueprint = Blueprint('news', __name__, url_prefix='/news', template_folder='templates', static_folder='static')

@blueprint.route("/society/")
def society_news():
    return render_template('society.html')

@blueprint.route("/tech/")
def tech_news():
    return "IT 新闻板块"

在第 3 行,创建一个名为 ‘news’ 的蓝图:

  • 蓝图中页面的 URL 前缀为 /news;
  • 蓝图的模板目录为 templates,绝对路径为 ‘项目目录 /news/templates’;
  • 蓝图的静态文件目录为 static,绝对路径为 ‘项目目录 /news/static’。

在第 5 行,将路径 /society/ 和函数 society_news 关联;在第 9 行,将路径 /tech/ 和函数 tech_news 关联。

在第 7 行,调用 render_template (‘society.html’) 渲染模板文件 society.html,根据模板文件的查找规则,最终在 ‘项目目录 /news/templates’ 目录下找到模板文件。

4.4 实现 news/templates/society.html

访问页面 /news/society 时,返回模板文件 news/templates/society.html:

<link rel="stylesheet" href="{{ url_for('news.static',filename='news.css')}}">
<h1>社会新闻</h1>

在模板文件中引用了静态文件 news.css。{{url_for (‘news.static’,filename=‘news.css’) }} 的输出为 news/static/news.css,其中 news.static 表示蓝图 news 的 static 目录。

4.5 实现 news/static/news.css

蓝图 news 中所有的 html 文件共享一个相同的 css 文件 —— news/static/news.css:

h1 {
    color: red;
}

将 h1 标签设置为红色,在 4.7 小节,可以看到运行截图,显示出 h1 标签中的文字为红色。

4.6 实现 products/__init.py__

和蓝图 products 相关的所有文件均放置在项目的 products 目录下,在主程序中导入蓝图 products.py 时,会执行 products/__init.py__:

from flask import Blueprint

blueprint = Blueprint('products', __name__, url_prefix='/products')

@blueprint.route("/car")
def car_products():
    return "汽车产品版块"

@blueprint.route("/baby")
def baby_products():
    return "婴儿产品版块"    

在第 3 行,创建一个名为 ‘products’ 的蓝图:

  • 蓝图中页面的 URL 前缀为 /products
  • 蓝图的模板目录为 templates,绝对路径为 ‘项目目录 /products/templates’;
  • 蓝图的静态文件目录为 static,绝对路径为 ‘项目目录 /products/static’。

在第 5 行,将路径 /car/ 和函数 car_products 关联;在第 9 行,将路径 /baby/ 和函数 baby_products 关联。

4.7 运行程序

在浏览器中,访问 http://localhost:5000/news/society/, 显示如下:

图片描述

因为 news.css 定义 h1 标签为红色,因此显示是红色的。

4.8 源代码下载

5. 小结

蓝图 (Blueprint) 是 Flask 程序的模块化处理机制,通过使用蓝图为 Flask 程序提供了一个可扩展性好的结构。随着业务代码的增加,需要在 Flask 程序中使用蓝图来应对程序的复杂性的增加。