16

我使用应用工厂模式定义我的 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()
4

3 回答 3

15

flask命令是一个 Click 界面,使用flask.cli.FlaskGroup. 创建您自己的组并将其传递给工厂函数。用于app.shell_context_processor将对象添加到外壳。

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命令与您的项目是分开的,因此它不受模块中的错误的影响。

于 2017-03-13T14:56:25.757 回答
2

为了将参数传递给您的应用程序工厂,您需要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_pointssetup.py@davidism 提到的或运行之后pip install manage.py)。

于 2020-04-09T22:16:51.057 回答
0

Flask >= 2.1 的最新更新。请参阅我对 Flask < 2.1 的其他答案。

为了将参数传递给我们的应用程序,我们将它们存储在script_info. 为了做到这一点,我们使用flask.cli.FlaskGroup.

但是,script_info直接传递给应用工厂在 Flask 2 中已被弃用,因此我们使用 Click 的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()

现在您可以运行开发服务器并config_mode使用-m或设置您想要的--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
于 2022-02-23T15:16:35.277 回答