Creating Commands with Application Context
To define custom CLI commands that automatically run within the Flask application context, use the AppGroup class. This ensures that commands have access to current_app, g, and other context-bound objects without manually pushing a context.
Using the Application or Blueprint CLI
The most common way to use AppGroup is through the built-in .cli attribute on Flask and Blueprint objects. Both of these attributes are instances of AppGroup.
from flask import Flask, Blueprint, current_app
app = Flask(__name__)
bp = Blueprint("tools", __name__)
# Registering a command on the app
@app.cli.command("hello")
def hello_command():
# Automatically runs within app context
print(f"Hello from {current_app.name}")
# Registering a command on a blueprint
@bp.cli.command("check")
def check_command():
# Automatically runs within app context
print(f"Checking {current_app.name}")
app.register_blueprint(bp)
Creating Custom Command Hierarchies
If you need a standalone CLI structure that still requires the Flask application context, you can instantiate AppGroup manually. This is useful for complex tools that exist outside the standard flask command entry point but still interact with your app.
import click
from flask.cli import AppGroup
from flask import current_app
@click.group(cls=AppGroup)
def my_custom_cli():
"""A custom group that provides app context to all sub-commands."""
pass
@my_custom_cli.command("status")
def status():
# This command automatically has access to current_app
click.echo(f"App is: {current_app.name}")
Nesting Groups
When you use AppGroup.group(), the resulting sub-group is also an instance of AppGroup. This means the application context requirement propagates down the entire command tree automatically.
@my_custom_cli.group()
def database():
"""Database management commands."""
pass
@database.command("init")
def init_db():
# This nested command also runs within the app context
click.echo(f"Initializing database for {current_app.name}")
Opting Out of Application Context
If you have a specific command within an AppGroup that does not require the application context (for example, a command that only prints a version string), you can disable the automatic wrapping by passing with_appcontext=False.
@app.cli.command("version", with_appcontext=False)
def version():
# This command runs WITHOUT an application context
click.echo("v1.0.0")
Troubleshooting
Missing Application Context
If you use AppGroup manually (outside of app.cli or bp.cli), you must ensure that a ScriptInfo object is provided to the Click context. The AppGroup relies on with_appcontext, which looks for the application factory or instance inside the ScriptInfo object.
In tests, this is typically handled by passing the obj parameter to the runner:
from flask.cli import ScriptInfo
def test_custom_command(runner):
obj = ScriptInfo(create_app=lambda: Flask("testapp"))
result = runner.invoke(my_custom_cli, ["status"], obj=obj)
assert "App is: testapp" in result.output
AppGroup vs FlaskGroup
Do not confuse AppGroup with FlaskGroup.
AppGroup: Aclick.Groupthat wraps its commands in the application context.FlaskGroup: A specialized group used for the mainflaskexecutable itself, which handles loading the application from environment variables likeFLASK_APP.