Defino mi aplicación Flask usando el patrón de fábrica de aplicaciones. Cuando uso Flask-Script, puedo pasar la función de fábrica al Manager . En su lugar, me gustaría usar la CLI de clic integrada de Flask. ¿Cómo uso la fábrica con Click?
Mi código actual usa Flask-Script. ¿Cómo hago esto con 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()El comando flask es una interfaz de clic creada con flask.cli.FlaskGroup . Crea tu propio grupo y pásale la función de fábrica. Use app.shell_context_processor para agregar objetos al 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() Ejecute su archivo en lugar del comando flask . Obtendrá la interfaz Click usando su fábrica.
FLASK_DEBUG=1 python app.py run Idealmente, cree un punto de entrada e instale su paquete en su env. Luego puede llamar al script como un comando. Cree un archivo setup.py con al menos lo siguiente.
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 Usar su propia CLI es menos robusto que el comando de flask incorporado. Debido a que su objeto cli está definido con su otro código, un error a nivel de módulo hará que el recargador falle porque ya no puede importar el objeto. El comando del flask está separado de su proyecto, por lo que no se ve afectado por errores en su módulo.
Para pasar argumentos a su fábrica de aplicaciones, necesita hacer uso de script_info así...
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:' ahora, en la línea de comando, puede manage -m Production run (después de agregar los puntos de setup.py entry_points se mencionó en @davidism, o ejecutar pip install manage.py ).
Recientemente actualizado para Flask >= 2.1. Vea mi otra respuesta para Flask <2.1.
Para pasar argumentos a nuestra aplicación, los almacenamos en script_info . Y para hacer eso, creamos una interfaz de clic personalizada usando flask.cli.FlaskGroup .
Sin embargo, pasar script_info directamente a las fábricas de aplicaciones está obsoleto en Flask 2 , por lo que usamos la función get_current_context de Click para obtener el contexto actual y luego acceder a script_info desde ese contexto.
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, eg, 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() Ahora puede ejecutar el servidor de desarrollo y configurar el config_mode deseado usando -m o --config-mode . Tenga en cuenta que hasta que Flask 2.1 caiga, debe instalar Flask@aa13521d42bfdb
pip install git+https://github.com/pallets/flask.git@aa13521d42bfdb python manage.py -m Production run Los servidores de producción como gunincorn no tienen acceso al contexto actual, por lo que pasamos lo que necesitamos a través kwargs .
gunicorn app:create_app\(config_mode=\'Production\'\) -w 3 -k gevent