【问题标题】:Use Flask's Click CLI with the app factory pattern将 Flask 的 Click CLI 与应用工厂模式一起使用
【发布时间】:2017-03-12 23:15:38
【问题描述】:

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

【问题讨论】:

    标签: python flask command-line-interface python-click flask-script


    【解决方案1】:

    Flask >= 2.1 的最新更新。请参阅我对 Flask

    为了将参数传递给我们的应用程序,我们将它们存储在 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
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        为了向您的应用工厂传递参数,您需要像这样使用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)。

        【讨论】:

          猜你喜欢
          • 2020-09-26
          • 2013-03-30
          • 1970-01-01
          • 2019-06-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-30
          • 2014-08-16
          相关资源
          最近更新 更多