flask组件化开发:蓝图

Flask 蓝图介绍

蓝图也就是 BluePrint 是 Flask 提供的一个类,它具备 Flask 核心对象的很多功能,其中最重要的就是注册路由,我们通过蓝图,可以将视图函数根据不同的功能拆分到不同的模块中,从而实现对视图函数的模块化管理与开发。

简单来说,我们可以把蓝图认为是一个完整 Web 应用的一部分,以个人Blog为例,它可以有Blog的展示部分,也需要有后台部分,此外还有登陆的部分,这些不同的部分就可以通过蓝图来进行拆分管理,它们组合起来就是一个完整的 Web 应用。

创建蓝图

先不忙创建蓝图,既然蓝图的作用是分模块存放视图函数,那么我们肯定需要创建 Python 模块以及 Python 包,本文所用的示例结构是如下这样的:

.
├── demo # 应用包
│ ├── __init__.py
│ └── blueprints # 蓝图包
│ ├── home.py # 首页蓝图模块
│ ├── admin.py # 后台蓝图模块
│ └── __init__.py
└── app.py # 入口文件

接下来,就可以创建蓝图了,在 home.py 中增加如下代码:

# demo/blueprints/home.py
from flask import Blueprint
 
home = Blueprint("home", __name__, url_prefix="/home")
 
@home.route('/')
def index():
return "This is home index page."

上面的代码中,我们首先实例化了一个 Blueprint 对象,和实例 Flask 核心对象不同,Blueprint 多了两个额外的参数:

  • "home": 蓝图的名称,这是一个必须的参数,之前在 Flask 路由与视图函数 一文的路由机制中,我专门谈到了 Flask 中 endpoint 端点的作用,这里的蓝图名称就是构成视图函数 endpoint 的一部分。
  • url_prefix="/home": 该蓝图下所有路由的前缀地址,比如这里定义的前缀是 /home,那么如果这个蓝图下有一个 /hello 路由,那么它的完整 URL 地址就是这样的 127.0.0.1:5000/home/hello,这里要注意的是,url_prefix 并非是必须的参数,我们也可以在注册蓝图的时候定义前缀地址,后面会提到。

除了上面这些,蓝图在注册路由方面和 Flask 核心对象是一模一样的,因为蓝图在注册路由的时候调用的也是 add_url_rule 这个方法,只不过此时这个方法并不是来自 Flask 核心对象了。

注册蓝图

注册蓝图就比较简单了,在 app.py 中,添加如下代码:

# app.py
from flask import Flask
 
from demo.blueprints.home import home
 
app = Flask(__name__)
 
# 注册蓝图
app.register_blueprint(home)

注册蓝图只需要调用 Flask 核心对象的 register_blueprint 方法即可。

接着我们就可以测试一下效果了,在命令行启动 Flask 服务:

$ export FLASK_APP=app.py
$ export FLASK_ENV=development
$ flask run

访问 http://127.0.0.1:5000/home/ 可以看到如下页面:

Flask 入门 6 —— Flask 蓝图详解 (Flask Blueprint) 1

注册蓝图时设置 URL 前缀

前面创建蓝图的时候,url_prefix 是定义在了实例化 Blueprint 的时候,除了这种方式,我们还可以在注册蓝图的时候定义 url_prefix,首先把实例化蓝图时定义的 url_prefix 删除掉:

...
# 删除 url_prefix
home = Blueprint("home", __name__)
...

接着在注册蓝图的时候,添加 url_prefix 参数

...
# 注册蓝图时设置 url_prefix 参数
app.register_blueprint(home, url_prefix="/home")
...

不管是在注册蓝图的时候设置 url_prefix 还是在实例化蓝图的时候设置,这两种方式的结果都是一样的,在编写代码的时候选择其中一种即可,不过如果蓝图特别特别多或者你比较习惯 Django 那种路由管理模式那么在注册蓝图的时候设置 url_prefix 是个好选择,毕竟如果蓝图多了的话修改某些蓝图的 URL 前缀肯定是在一个统一的地方比较方便。

endpoint 端点

在继续接下来的内容之前,我想在这里演示一下 Flask 对于 endpoint 的处理。

上面我们已经定义了一个蓝图,并且注册了一个路由,接下来在 admin.py 中,我们再定义一个蓝图并为其注册一个路由。

# demo/blueprints/admin.py
from flask import Blueprint
 
admin = Blueprint("admin", __name__, url_prefix="/admin")
 
@admin.route("/")
def index():
return "This is admin index page."

然后把这个蓝图注册给 Flask 核心对象

# app.py
from flask import Flask
 
from demo.blueprints.home import home
from demo.blueprints.admin import admin
 
app = Flask(__name__)
 
app.register_blueprint(home)
app.register_blueprint(admin)

此时就有了两个蓝图,这两个蓝图中都有一个视图函数 index ,之前在 Flask 路由与视图函数 这篇文章中,我就提到 Flask 通过 endpoint 来指向视图函数,这种机制是为了避免视图函数在命名空间上的冲突,下面到 flask shell 中,执行如下代码:

$ flask shell
 
In [1]: from app import app
In [2]: app.url_map
Out[2]:
Map([<Rule '/admin/' (GET, OPTIONS, HEAD) -> admin.index>,
<Rule '/home/' (GET, OPTIONS, HEAD) -> home.index>])

通过 url_map 属性可以查询到当前的 Flask 应用所有的路由地址和其指向的 endpoint,在这里我们可以看到,当前的 Flask 应用一共有两个 URL :

  • /admin/ 是 URL 相对路径。
  • admin.index 就是 endpoint 了。

这里的重点是 URL 路径所指向的 endpoint,它是由蓝图的名称和视图函数名称共同构成的,如果没有 endpoint 当用户请求 http://127.0.0.1:5000/home/ 或者 http://127.0.0.1:5000/admin/ 的时候,因为存在两个 index 视图函数,直接通过 URL 找视图函数就出问题了,到底哪个 index 函数对应的是用户所请求 URL ?所以 Flask 才需要 endpoint 这种机制来处理 URL 和视图函数之间的映射。

蓝图资源

蓝图除了可以将路由模块化,一样也可以将对应的静态文件和模板模块化,在演示蓝图处理自身静态资源之前,我们还需要对当前的目录结构做一些改动,将我们之前创建的两个蓝图模块移动到各自对应的文件夹中,接着在 home 目录下创建 templates 和 static 目录用来存放该蓝图下的模板文件和静态文件,最后在 templates 目录下创建 index.html 并在 static 目录下创建 main.css

.
├── demo
│ ├── __init__.py
│ └── blueprints
│ ├── __init__.py
│ ├── admin
│ │ ├── __init__.py
│ │ └── admin.py
│ └── home
│ ├── __init__.py
│ ├── templates # 模板目录
│ └── home
│ └── index.html
│ ├── static # 静态资源目录
│ │ ├── main.css
│ └── home.py
└── app.py

接下来,还需要简单的改动一下 app.py 中的引用:

# app.py
from flask import Flask
 
from demo.blueprints.home.home import home
from demo.blueprints.admin.admin import admin
...

最后分别在 index.html 和 main.css 添加下面的代码

<!-- demo/blueprints/home/templates/home/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>This is home index page.</h1>
</body>
</html>
/* demo/blueprints/home/static/main.css */
h1 {
color: orange;
font-style: italic;
}

做好准备工作,就可以演示蓝图使用自身的模板和静态文件了。

蓝图模板

首先看下模板的使用,要想让蓝图正确的识别到这个模板,首先需要配置蓝图的模板路径,在实例化蓝图的时候,通过 template_folder 参数指定模板路径:

from flask import Blueprint, render_template
 
# 添加 template_folder 参数指定模板路径
home = Blueprint("home", __name__, url_prefix="/home",
template_folder="templates")
 
@home.route('/')
def index():
return render_template("home/index.html")

上述代码中,指定的模板路径是一个相对路径,这个路径是基于蓝图实例根目录的;此外在创建模板目录的时候,我特地在 templates 目录下多创建了一个 home 目录保存 index.html 这也是有原因的,如果 app.py 根目录下还有一个 templates 全局模板目录,而其中也有一个 index.html,那么此时全局的 index.html 的优先级是高于蓝图模板的优先级的,所以如果不添加一个 home 目录来区分蓝图模板和全局模板,那么 render_template("index.html") 这段代码会优先去使用 templates 下的 index.html

当然如果全局的 templates 目录下,也有 home 目录,也存在 index.html,那么还是会覆盖蓝图的 templates/home/index.html,所以这里就引发了是否有必要为蓝图单独创建模板目录的问题了,毕竟我们完全可以在全局 templates 目录中,通过文件夹的形式区分模板所属的蓝图,不过这个问题是仁者见仁的事情,我个人是不倾向于给蓝图单独指定模板目录的。

蓝图静态文件

演示了蓝图模板,再来看看蓝图的静态文件,蓝图独有的静态文件也需要和模板一样指定静态文件目录的路径:

...
 
# 添加 static_folder 参数指定静态文件路径
home = Blueprint("home", __name__, url_prefix="/home",
template_folder="templates",
static_folder="static")
 
...

通过 static_folder 即可设置蓝图的静态文件路径,和模板路径的设置差不多,指定相对路径即可。

接着,到 index.html 中,我们需要引用静态文件了:

...
<head>
...
<link rel="stylesheet" href="{{ url_for('home.static', filename='main.css') }}">
</head>
...

和引用全局静态文件不同,引用蓝图独有的静态文件时,我们需要指定蓝图的前缀 home.static,这样 Flask 才会正确的前往蓝图的 /static 下寻找静态文件;此外和模板不同的是,蓝图不存在优先级的问题,指定了前缀之后,它就只会在蓝图中搜索静态文件。

最后,看一眼网页,我们单独为蓝图设置的模板和静态文件工作的还是正常的:

Flask 入门 6 —— Flask 蓝图详解 (Flask Blueprint) 2

转载:https://www.hizxc.com/1563.html#zhu_ce_lan_tu

 
posted @ 2020-11-04 12:30  -零  阅读(384)  评论(0编辑  收藏  举报