Creating custom commands in flask needs access to the app, which is generally created in app.py like this:
import click
from flask import Flask
app = Flask(__name__)
@app.cli.command("create-user")
@click.argument("name")
def create_user(name):
...
However, in order not to bloat my app.py, I want to put my custom commands in a separate file e.g. commands.py, but this doesn't work because the entrypoint to my project is app.py, so I'll have to import app in commands.pyand import my commands in app.py which results in a circular import error.
How can I create custom commands in separate files ?
One way to achieve this would be using blueprints
I have tested it using Flask 1.1.1, so be sure to check the documentation of the correct version that you have.
Here is the general idea:
==> 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))
Upon executing flask users you should get a response like the following:
flask users
Usage: flask users [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
create Creates a user
just import it in your app factory
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
but there have to be better way ;) of which I am unaware right now
I have this layout:
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()
In the console where you run the commands first define the FLASK_APP to be commands.py, then run the commands that you define.
set FLASK_APP=commands.py
export FLASK_APP=commands.py
flask create-super-user me
You can either use a separate terminal for built-in commands or clear the FLASK_APP variable before issuing them. In Linux is even easier because you can do
FLASK_APP=commands.py flask create-super-user me
What worked for me in case you are not using app factory pattern, similar to @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')
If you're using an app factory (you have a create_app() function), then there isn't even an app variable for you to import.
The best way to keep your code organized is to define the function somewhere else, and then register it when building the application instance.
E.g.
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()
...