Flask中处理依赖的技巧
Flask应用中通常会用工厂模式 来创建应用对象,这样方便配置和测试。
应用代码
# app/__init__.py
from flask import Flask
from flask_xxxext import Xxx
from flask_yyyext import Yyy
# ... 一些flask拓展
xx = Xxx()
yy = Yyy()
def create_app(config=None):
app = Flask(__name__)
xx.init_app(app)
yy.init_app(app)
启动脚本
# manage.py
from app import create_app
app = create_app()
if __name__ == "__main__":
app.run()
测试代码
# test_app.py
from app import create_app
def test_xxx():
app = create_app()
# ... tests
在稍大一些的项目里,不可避免会用到许多flask插件,也会用到一些其他的库, 这些库通常没有特意去支持flask的应用工厂模式。
应用代码不仅依赖于这些外部的库,也会产生一些相互依赖。比如A模块依赖B模块,B模块又依赖A模块, 这样在导入模块的时候就会遇到循环导入的问题,有时还会产生A依赖B,B依赖C…N依赖A这样复杂的依赖关系。
一方面就是应用架构的问题,要解决这类依赖问题,首先是要让整体架构清晰,各个模块直接形成清晰的 职责边界,不要在同一个模块做职责不同的事。 比如A模块依赖B模块,B模块又依赖A模块,这种情况就是A模块或B模块做了职责之外的事,把它们 职责之外的事拆分出来,放到C模块中,问题就解决了。
另一方面就是使用全局对象带来的依赖问题,flask中通常会定义一些全局对象,在要用到的地方 直接导入需要的对象,这样使用的时候很方便。flask的插件基本都支持这样用,因为这些插件都没有 显式的依赖,而是提供一个init_app
方法,用于在运行时初始化插件。
但是还会用到一些其他的库,这些库没有提供init_app
方法。 有两种办法,第一种就是写一个新的类,把原来的库包装一下,提供一个init_app
方法用来延迟初始化。 这种方法有点繁琐,也不灵活,对每一个依赖都要写一个类。
另一种方法就是用 werkzeug.local.LocalProxy
实现延迟初始化。
所有被依赖的全局对象
# app/dependency.py
from werkzeug.local import LocalProxy
class Dependency:
"""Dependency"""
d = Dependency()
xx = LocalProxy(lambda: d.xx)
yy = LocalProxy(lambda: d.yy)
__all__ = ['d', 'xx', 'yy']
应用代码
# app/__init__.py
from xxx import Xxx
from yyy import Yyy
from dependency import d
def create_app(config=None):
app = create_app()
d.xx = Xxx(...)
d.yy = Yyy(...)
# app/some_module.py
from .dependency import xx
def view():
# 业务代码
xx.xxxx()
使用这种方式方便又灵活,唯一的限制是被依赖的全局对象只能在应用初始化之后使用,不过这些业务代码都是 在接受到请求才会执行,这时候应用早就初始化了,所以这点限制也没什么影响。