初学flask时,惊艳于flask的小巧和简洁,对于一个接口性质,尤其主要功能都是由脚本完成的项目而言,相较于臃肿的Django而言,flask简直是天作之合。
大道至简
# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/xxx/yyy/<int:id>", methods=["POST", "GET"])
def test(id):
print(id)
return "your parameter is %d" % id
if __name__ == "__main__":
app.run()
没错,最基本的代码就是这么一个文件,这么几行代码。只需要把test函数改为你的调用入口,一个基于脚本功能的API接口就搭建起来了!
想输出json格式也很简单,可以导入jsonify,然后格式化一下,就像这样:
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/xxx/yyy/<int:id>", methods=["POST", "GET"])
def test(id):
return jsonify(method="login")
if __name__ == "__main__":
app.run()
你就能得到这样的效果啦:
{"method":"login"}
less is more
但是这里面有一个问题,就是所有文件我们都写在了这个带着app.run()方法的文件里,这个叫app.py的文件是flask项目的启动入口。如果我们的API很多怎么办呢?比如这样的目录结构:
- account:
- login
- logout
- register
- user:
- update_profile
- change_password
- order:
- order_list
- order_detail
......
我们实际中写的项目的规模远远大于上面我写的东西。那么,问题来了:
- 一个单文件,但代码量却在万行以上的项目,要我们如何维护?
- 如何实现中间件功能,比如访问修改用户资料时先检查用户是否登录呢?
- 如何分工协作?
交给蓝图吧
以上种种问题,蓝图都是可以搞定的。
怎么理解蓝图呢,我想用零售行业的例子来解释一下。
之前的那个app.py相当于厂家。厂家我们把所有功能都写在里面,就好比这个厂家是生产销售一把抓。
蓝图(Blueprint)可以被看做是一个代理商,厂家把生产出来的产品直接给代理商就行了。厂家可以专心做生产,销售任务就全交给代理商就好了。
代理商不负责终端销售,一般我们买东西访问的门店都分布在各个商圈。这些门店,厂家是不关心的。
所以这个模式,就是一个厂家对几个代理商,每个代理商对几个门店,呈现出一个树形结构。
同理,在flask里面,一个app.py对应多个蓝图,每个蓝图再对应多个视图函数。
如图所示:
还是基于上面的user, account, order的需求,我们用蓝图来实现,目录结构可以是这样的:
- app.py
- views
- _init_.py
- account.py
- user.py
- order.py
每个文件大概是这个样子:
- app.py变得很纯粹了,从views目录导入了Flask示例(app对象),然后执行。
# app.py
from views import app
if __name__ == '__main__':
app.run()
- 我们在views目录的init文件里实例化了Flask对象,导入了每个功能块的蓝图实例(blue_account对象等),并将这些蓝图实例注册到了flask实例中。
# views/__init__.py
from flask import Flask
from .account import blue_account
from .user import blue_user
from .order import blue_order
app = Flask(__name__)
app.register_blueprint(blue_account)
app.register_blueprint(blue_user)
app.register_blueprint(blue_order)
- 以account为例,我们实例化了一个蓝图实例(blue_account),并通过蓝图实例的route方法进行url和函数的映射。
蓝图具有app的大部分方法,我们基本都可以直接平移使用。
不仅如此,我们在实例化蓝图对象时,还可以指定url_prefix参数,实现url地址的目录化,进行逻辑归类。
# views/account.py
from flask import Blueprint, jsonify
blue_account = Blueprint("account", __name__, url_prefix="/account")
@blue_account.route("/login", methods=["GET", "POST"])
def login():
return jsonify(method="login")
@blue_account.route("/logout", methods=["GET"])
def logout():
return "logout"
如此一来,每一个文件的代码都不会看起来冗长,且不同的逻辑进行了物理分离。这样的代码,条理清晰,即能够让不同的人负责不同的模块,修改起来也能很容易的找到相应的代码了。
如果我要实现中间件呢?
没问题。以user为例。我们可以通过before_request装饰器来实现。
from flask import Blueprint
blue_user = Blueprint("user", __name__, url_prefix="/user")
# 通过before_request实现类似中间的功能
@blue_user.before_request
def check_auth():
if not session.get("auth"):
return redirect("/account/login")
@blue_user.route("/update_profile", methods=["GET", "POST"])
def update_profile():
return "update_profile"
增加蓝图功能的注意事项
- 迁移步骤
如果你之前所有的代码都写在了app.py里,那么看完这篇文章后,你就可以做迁移了。迁移的步骤大概包括:
- 在app.py平级目录下,创建一个新的目录用来放置功能代码;
- 在业务目录下,根据业务分类,新建N个py文件,并将对应的代码剪切过来;
- 在py文件的上方,实例化蓝图对象,并把原先用app写的装饰器,改为蓝图;
- 在业务目录下,新建_init_.py,把实例化flask的代码放进来,并导入步骤3中创建出来的蓝图对象;
- 注意事项
- 如果你使用了文件配置,要注意sys.path里,默认的项目根目录仍然在app.py所在的目录中。所以修改时,需要带上目录名。
app = Flask(__name__)
# 原先的写法,导入settings.py里的DevConfig块
# app.config.from_object("settings.DevConfig")
# 使用蓝图后,settings.py会放入业务目录中做配置分离,那么导入时要加上views目录才行
app.config.from_object("views.settings.DevConfig")
- 如果在页面或跳转时使用了别名,也需要加上蓝图的名字
# 之前如果是这么写的
def checkAuth():
# 判断逻辑
return redirect( url_for("login") )
# 使用蓝图之后,需要这么写
def checkAuth():
# 判断逻辑
return redirect( url_for("account.login") )
- 实例化蓝图对象的小细节:
(1)前两个参数是必填的,第一个参数是name,也就是我们用url_for的时候使用的名字;
(2)第二个参数是import_name,一般都写name,其实就是文件名;
(3)返回值用于被_init_.py中做导入;
(4)参数可以做定制化,如url_prefix用于做url的目录结构划分,template_folder可以自定义模板目录,static_folder可以自定义静态文件目录;
学会了蓝图,我们的flask项目就可以实现类似Django的多APP模块分离的结构了,这样有利于我们做功能划分,url目录结构化,配置隔离化等等,flask才真的做到了能屈能伸。