Flask 上的 Web 应用:如何处理循环导入





5.00/5 (1投票)
关于 Python 和 Flask 的一些内容
Flask和循环导入
在使用Flask时,开发人员经常面临模块之间依赖关系的问题。为了创建视图和模型,开发人员使用在主模块(在“前端控制器”中)中创建和初始化的全局对象。 同时,存在发生循环导入的风险,并且难以维护项目。
Flask 文档和基本教程建议在__init__.py 中编写项目初始化代码以解决问题。 此代码创建Flask类的实例并配置应用程序。 它允许从包的可见区域访问所有全局对象。
使用这种方法,结构如下所示
.
├── app
│ ├── __init__.py
│ ├── forms.py
│ ├── models.py
│ ├── views.py
│ └── templates
├── config.py
└── migrations
app/__init__.py
import flask
from flask_mail import Mail
# other extensions
app = Flask(__name__)
mail = Mail(app)
# configure flask app
from app import views, models
app/views.py
from app import app
@app.route('/view_name/'):
def view_name():
pass
显然,这种架构不是很好,因为所有组件都紧密相连。 随后,很难详细说明这样的项目,因为在一个地方更改代码将导致在十几个其他地方进行更改。
通常,我们按如下方式解决问题
- 我们避免标准路由。
- 我们更喜欢库的原始版本,没有“包装”。
- 使用依赖注入。
让我们更深入地关注这一点。
使用Classy
您可以采用classy方法,而不是使用文档中描述的标准路由方法。 使用这种方法,您不必手动编写视图的路由:它将根据您的类和方法的名称自动配置。 这种方法可以改善代码的结构,以及创建没有应用程序对象的视图。 结果,解决了循环导入的问题。
使用flask-classful库时,项目结构的示例
.
├── app
│ ├── static
│ ├── templates
│ ├── forms.py
│ ├── routes.py
│ ├── views.py
│ └── tasks.py
├── models
├── app.py
├── config.py
└── handlers.py
app.py
import flask
from flask_mail import Mail
# other extensions
from app import routes as app_route
app = Flask(__name__)
mail = Mail(app)
# configure flask app
app.register_blueprint(app_route.app_blueprint)
app/routes.py
from flask import Blueprint
from app import views
app_blueprint = Blueprint(...)
views.AccountView.register(app_blueprint)
# register other views
app/views.py
from flask_classy import FlaskView, route
from flask_login import login_required
class AccountView(FlaskView):
def login(self):
pass
# other views
@login_required
def logout(self):
pass
在检查代码时,您应该注意初始化现在发生在位于根目录的app.py中。 应用程序分为子项目,这些子项目由蓝图配置,然后仅用一行代码注册到应用程序对象中。
原始库更受欢迎
上面提供的代码显示了flask-classful如何帮助应对循环导入。 经典Flask项目中出现此问题的原因是视图声明和某些扩展。 最好的例子之一是flask-sqlalchemy
。flask-sqlalchemy
扩展旨在改善sqlalchemy
和flask
之间的集成,但实际上,它带来的问题多于好处
- 该扩展促进了使用全局对象来处理数据库,包括模型的创建,这再次导致循环导入的问题。
- 需要使用您自己的类来描述模型,这导致模型与Flask项目的紧密绑定。 结果,这些模型不能在子项目或支持脚本中使用。
由于这些原因,我们尽量不使用flask-sqlalchemy
。
使用依赖注入模式
实现classy方法和拒绝flask-sqlalchemy
只是解决循环导入问题的第一步。 接下来,您需要实现逻辑以获取应用程序中全局对象的访问权限。 为此,最好使用在dependency-injector library中实现的依赖注入模式。
在带有dependency-injector
库的代码中使用模式的示例
app.py
import dependency_injector.containers as di_cnt
import dependency_injector.providers as di_prv
from flask import Flask
from flask_mail import Mail
from app import views as app_views
from app import routes as app_routes
app = Flask(__name__)
mail = Mail(app)
# blueprints registration
app.register_blueprint(app_routes.app_blueprint)
# providers creation
class DIServices(di_cnt.DeclarativeContainer):
mail = di_prv.Object(mail)
# injection
app_views.DIServices.override(DIServices)
app/routes.py
from os.path import join
from flask import Blueprint
import config
from app import views
conf = config.get_config()
app_blueprint = Blueprint(
'app', __name__, template_folder=join(conf.BASE_DIR, 'app/templates'),
static_url_path='/static/app', static_folder='static'
)
views.AccountView.register(app_blueprint, route_base='/')
app/views.py
import dependency_injector.containers as di_cnt
import dependency_injector.providers as di_prv
from flask_classy import FlaskView
from flask_login import login_required
class DIServices(di_cnt.DeclarativeContainer):
mail = di_prv.Provider()
class AccountView(FlaskView):
def registration(self):
# registration implementation
msg = 'text'
DIServices.mail().send(msg)
def login(self):
pass
@login_required
def logout(self):
pass
本文中提到的措施可以消除循环导入,并提高代码质量。 我们建议通过以web app形式设计的“公牛母牛”游戏的示例,使用上述方法查看Flask项目。
结论
我们已经考虑了克服与循环导入相关的Flask应用程序的常见架构问题的技巧。 使用它们,您可以简化应用程序的维护和重构。
感谢您的关注! 我们希望本文对您有所帮助。
历史
- 2020 年 4 月 23 日:初始版本