将 Flask 的 Click CLI 与应用工厂模式结合使用
Use Flask's Click CLI with the app factory pattern
我使用应用工厂模式定义我的 Flask 应用程序。使用 Flask-Script 时,我可以将工厂函数传递给 Manager
。我想改用 Flask 的内置 Click CLI。如何将工厂与 Click 一起使用?
我当前的代码使用 Flask-Script。我如何使用 Click 执行此操作?
from flask import Flask
from flask_script import Manager, Shell
def create_app():
app = Flask(__name__)
...
return app
manager = Manager(create_app)
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command('shell', Shell(make_context=make_shell_context))
if __name__ == '__main__':
manager.run()
flask
命令是用flask.cli.FlaskGroup
创建的Click界面。创建您自己的组并将其传递给工厂函数。使用 app.shell_context_processor
将对象添加到 shell。
from flask import Flask
from flask.cli import FlaskGroup
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(script_info=None):
app = Flask(__name__)
db.init_app(app)
...
@app.shell_context_processor
def shell_context():
return {'app': app, 'db': db}
return app
cli = FlaskGroup(create_app=create_app)
@cli.command
def custom_command():
pass
if __name__ == '__main__':
cli()
运行 您的文件而不是 flask
命令。您将使用您的工厂获得 Click 界面。
FLASK_DEBUG=1 python app.py run
理想情况下,创建一个入口点并将您的包安装到您的环境中。然后您可以将脚本作为命令调用。创建至少包含以下内容的 setup.py
文件。
project/
app/
__init__.py
setup.py
from setuptools import setup, find_packages
setup(
name='my_app',
version='1.0.0',
packages=find_packages(),
entry_points={
'console_scripts': [
'app=app:cli',
],
},
)
pip install -e /path/to/project
FLASK_DEBUG=1 app run
使用您自己的 CLI 不如内置 flask
命令可靠。因为您的 cli
对象是用您的其他代码定义的,所以模块级错误将导致重新加载程序失败,因为它无法再导入该对象。 flask
命令独立于您的项目,因此它不受模块错误的影响。
为了将参数传递给您的应用程序工厂,您需要像这样使用 script_info
...
manage.py
#!/usr/bin/env python
import click
import config
from flask import Flask
from flask.cli import FlaskGroup, pass_script_info
def create_app(script_info):
app = Flask(__name__)
if script_info.config_mode:
obj = getattr(config, script_info.config_mode)
flask_config.from_object(obj)
...
return app
@click.group(cls=FlaskGroup, create_app=create_app)
@click.option('-m', '--config-mode', default="Development")
@pass_script_info
def manager(script_info, config_mode):
script_info.config_mode = config_mode
if __name__ == "__main__":
manager()
config.py
class Config(object):
TESTING = False
class Production(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class Development(Config):
DATABASE_URI = 'sqlite:///app.db'
class Testing(Config):
TESTING = True
DATABASE_URI = 'sqlite:///:memory:'
现在您可以在命令行中执行 manage -m Production run
(在将 entry_points
添加到 setup.py
之后,如 @davidism 提到的,或者 运行 pip install manage.py
).
Flask >= 2.1 的最新更新。请参阅我对 Flask < 2.1 的其他回答。
为了将参数传递给我们的应用程序,我们将它们存储在 script_info
中。为了做到这一点,我们使用 flask.cli.FlaskGroup
.
创建了一个自定义的 Click 界面
但是,直接将script_info
传递给应用程序工厂是deprecated in Flask 2,所以我们使用
单击 get_current_context
函数获取当前上下文,然后从该上下文访问 script_info
。
manage.py
#!/usr/bin/env python
import click
import config
from click import get_current_context
from flask import Flask
from flask.cli import FlaskGroup, pass_script_info
def create_app(*args, **kwargs):
app = Flask(__name__)
ctx = get_current_context(silent=True)
if ctx:
script_info = ctx.obj
config_mode = script_info.config_mode
elif kwargs.get("config_mode"):
# Production server, e.g., gunincorn
# We don't have access to the current context, so must
# read kwargs instead.
config_mode = kwargs["config_mode"]
...
return app
@click.group(cls=FlaskGroup, create_app=create_app)
@click.option('-m', '--config-mode', default="Development")
@pass_script_info
def manager(script_info, config_mode):
script_info.config_mode = config_mode
...
if __name__ == "__main__":
manager()
现在您可以 运行 开发服务器并使用 -m
或 --config-mode
设置您想要的 config_mode
。请注意,在 Flask 2.1 发布之前,您必须安装 Flask@aa13521d42bfdb
pip install git+https://github.com/pallets/flask.git@aa13521d42bfdb
python manage.py -m Production run
gunincorn 等生产服务器无法访问当前上下文,因此我们通过 kwargs
.
传递我们需要的内容
gunicorn app:create_app\(config_mode=\'Production\'\) -w 3 -k gevent
我使用应用工厂模式定义我的 Flask 应用程序。使用 Flask-Script 时,我可以将工厂函数传递给 Manager
。我想改用 Flask 的内置 Click CLI。如何将工厂与 Click 一起使用?
我当前的代码使用 Flask-Script。我如何使用 Click 执行此操作?
from flask import Flask
from flask_script import Manager, Shell
def create_app():
app = Flask(__name__)
...
return app
manager = Manager(create_app)
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command('shell', Shell(make_context=make_shell_context))
if __name__ == '__main__':
manager.run()
flask
命令是用flask.cli.FlaskGroup
创建的Click界面。创建您自己的组并将其传递给工厂函数。使用 app.shell_context_processor
将对象添加到 shell。
from flask import Flask
from flask.cli import FlaskGroup
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(script_info=None):
app = Flask(__name__)
db.init_app(app)
...
@app.shell_context_processor
def shell_context():
return {'app': app, 'db': db}
return app
cli = FlaskGroup(create_app=create_app)
@cli.command
def custom_command():
pass
if __name__ == '__main__':
cli()
运行 您的文件而不是 flask
命令。您将使用您的工厂获得 Click 界面。
FLASK_DEBUG=1 python app.py run
理想情况下,创建一个入口点并将您的包安装到您的环境中。然后您可以将脚本作为命令调用。创建至少包含以下内容的 setup.py
文件。
project/
app/
__init__.py
setup.py
from setuptools import setup, find_packages
setup(
name='my_app',
version='1.0.0',
packages=find_packages(),
entry_points={
'console_scripts': [
'app=app:cli',
],
},
)
pip install -e /path/to/project
FLASK_DEBUG=1 app run
使用您自己的 CLI 不如内置 flask
命令可靠。因为您的 cli
对象是用您的其他代码定义的,所以模块级错误将导致重新加载程序失败,因为它无法再导入该对象。 flask
命令独立于您的项目,因此它不受模块错误的影响。
为了将参数传递给您的应用程序工厂,您需要像这样使用 script_info
...
manage.py
#!/usr/bin/env python
import click
import config
from flask import Flask
from flask.cli import FlaskGroup, pass_script_info
def create_app(script_info):
app = Flask(__name__)
if script_info.config_mode:
obj = getattr(config, script_info.config_mode)
flask_config.from_object(obj)
...
return app
@click.group(cls=FlaskGroup, create_app=create_app)
@click.option('-m', '--config-mode', default="Development")
@pass_script_info
def manager(script_info, config_mode):
script_info.config_mode = config_mode
if __name__ == "__main__":
manager()
config.py
class Config(object):
TESTING = False
class Production(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class Development(Config):
DATABASE_URI = 'sqlite:///app.db'
class Testing(Config):
TESTING = True
DATABASE_URI = 'sqlite:///:memory:'
现在您可以在命令行中执行 manage -m Production run
(在将 entry_points
添加到 setup.py
之后,如 @davidism 提到的,或者 运行 pip install manage.py
).
Flask >= 2.1 的最新更新。请参阅我对 Flask < 2.1 的其他回答。
为了将参数传递给我们的应用程序,我们将它们存储在 script_info
中。为了做到这一点,我们使用 flask.cli.FlaskGroup
.
但是,直接将script_info
传递给应用程序工厂是deprecated in Flask 2,所以我们使用
单击 get_current_context
函数获取当前上下文,然后从该上下文访问 script_info
。
manage.py
#!/usr/bin/env python
import click
import config
from click import get_current_context
from flask import Flask
from flask.cli import FlaskGroup, pass_script_info
def create_app(*args, **kwargs):
app = Flask(__name__)
ctx = get_current_context(silent=True)
if ctx:
script_info = ctx.obj
config_mode = script_info.config_mode
elif kwargs.get("config_mode"):
# Production server, e.g., gunincorn
# We don't have access to the current context, so must
# read kwargs instead.
config_mode = kwargs["config_mode"]
...
return app
@click.group(cls=FlaskGroup, create_app=create_app)
@click.option('-m', '--config-mode', default="Development")
@pass_script_info
def manager(script_info, config_mode):
script_info.config_mode = config_mode
...
if __name__ == "__main__":
manager()
现在您可以 运行 开发服务器并使用 -m
或 --config-mode
设置您想要的 config_mode
。请注意,在 Flask 2.1 发布之前,您必须安装 Flask@aa13521d42bfdb
pip install git+https://github.com/pallets/flask.git@aa13521d42bfdb
python manage.py -m Production run
gunincorn 等生产服务器无法访问当前上下文,因此我们通过 kwargs
.
gunicorn app:create_app\(config_mode=\'Production\'\) -w 3 -k gevent