Где я должен реализовать колбу пользовательских команд (cli)

Создание пользовательских команд в колбе требует доступа к приложению, которое обычно создается в app.py нравится:

import click
from flask import Flask

app = Flask(__name__)

@app.cli.command("create-user")
@click.argument("name")
def create_user(name):
    ...

Однако, чтобы не раздувать мой app.py, я хочу поместить свои пользовательские команды в отдельный файл, например commands.py, но это не работает, потому что точка входа в мой проект app.pyтак что мне придется импортировать приложение в commands.pyи импортировать мои команды в app.py что приводит к циклической ошибке импорта.

Как я могу создавать собственные команды в отдельных файлах?

7 ответов

Один из способов добиться этого - использовать чертежи.

Я тестировал его с помощью Flask 1.1.1, поэтому не забудьте проверить документацию на правильную версию, которая у вас есть.

Вот общая идея:

  1. Создайте один или несколько Blueprints в другом файле, скажем, он называется commands.py
  2. Затем импортируйте новые чертежи и зарегистрируйте их в своем приложении.

==> app.py <==

from flask import Flask
from commands import usersbp

app = Flask(__name__)
# you MUST register the blueprint
app.register_blueprint(usersbp)

==> commands.py <==

import click
from flask import Blueprint

usersbp = Blueprint('users', __name__)

@usersbp.cli.command('create')
@click.argument('name')
def create(name):
    """ Creates a user """
    print("Create user: {}".format(name))

При выполнении flask users вы должны получить такой ответ:

flask users
Usage: flask users [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  create  Creates a user

Просто импортируйте его в свою фабрику приложений

dir tree

my_app
     L app.py
     L commands.py

commands.py

@app.cli.command('resetdb')
def resetdb_command():
    """Here info that will be shown in flask --help"""
    pass

app.py

def create_app():
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = DB_URL
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    db.init_app(app)

    with app.app_context():
        from . import routes
        from . import commands  # <----- here

        return app
$ export FLASK_APP=my_app/app.py
$ flask resetdb

но должен быть способ получше;) о котором я сейчас не знаю

Если вы используете фабрику приложений (у вас есть create_app() функция), то нет даже app переменная для импорта.

Лучший способ сохранить ваш код организованным - это определить функцию где-нибудь в другом месте, а затем зарегистрировать ее при создании экземпляра приложения.

Например

      my_app/
 | main.py
 | app/
 |  | __init__.py
 |  | commands.py

commands.py

      def foo():
   print("Running foo()")

init .py

      def create_app():
 app = Flask(__name__)
 ...
 from .commands import foo
 @app.cli.command('foo')
 def foo_command():
   foo()
 ...

Моя практика:

├── app.py

├── команды.py

в app.py:

      from commands import register_commands

def create_app():
    app = Flask(__name__)
    ....

    register_commands(app)
    return app

в командах.py

      from sqlalchemyseeder import ResolvingSeeder
from app import db

def register_commands(app):
    @app.cli.command("seed_courses")
    def seed_courses():
        seeder = ResolvingSeeder(db.session)
        seeder.load_entities_from_json_file("app/db/seed/data.json")
        db.session.commit()
        db.session.close()

    @app.cli.command("command2")
    def another_command():
        print("another command")

бегать:

      flask seed_courses
flask command2

Что сработало для меня, если вы не используете шаблон фабрики приложений, похожий на @quester:

app.py

      import os

from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("DATABASE_URL")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)
migrate = Migrate(app, db)

with app.app_context():
    # needed to make CLI commands work
    from commands import *

commands.py

      from app import app

@app.cli.command()
def do_something():
    print('hello i am so nice I posted this even though I have 100 other things to do')

У меня такой расклад:

baseapp.py

from flask import Flask

app = Flask("CmdAttempt")

app.py

from .baseapp import app

def main():
    app.run(
        port=5522,
        load_dotenv=True,
        debug=True
    )

if __name__ == '__main__':
    main()

commands.py

import click

from .baseapp import app


@app.cli.command("create-super-user")
@click.argument("name")
def create_super_user(name):
    print("Now creating user", name)


if __name__ == '__main__':
    from .app import main
    main()

В консоли, где вы запускаете команды, сначала определите FLASK_APP быть commands.py, затем выполните определенные вами команды.

set FLASK_APP=commands.py
export FLASK_APP=commands.py
flask create-super-user me

Вы можете использовать отдельный терминал для встроенных команд или очистить FLASK_APPпеременная перед их выдачей. В Linux это еще проще, потому что вы можете делать

FLASK_APP=commands.py flask create-super-user me

Мое решение:

приложение.py:

      from flask import Flask

def create_app():
    import commands
    application = Flask(__name__)

    application.cli.add_command(commands.hello)
    return application


if __name__ == '__main__':
    app = create_app()
    app.run()

команды.py:

      import click


@click.command("say_hello")
@click.argument("name")
def hello(name):
    """ Say hello to user """
    print(f"Hello {name}")
Другие вопросы по тегам