CLI y comandos personalizados en Flask

Intermedio
Flask
Flask
Actualizado: 18/04/2026

El sistema CLI de Flask

Flask incluye un sistema de línea de comandos (CLI) integrado basado en Click, la biblioteca de interfaces de línea de comandos más popular de Python. Este sistema permite ejecutar tareas administrativas directamente desde la terminal sin necesidad de escribir scripts separados.

Comandos CLI en Flask: crear comandos personalizados con Click

Al instalar Flask, el comando flask queda disponible en el entorno virtual y expone varios subcomandos integrados:

# Iniciar el servidor de desarrollo
flask run

# Abrir una consola Python con el contexto de la aplicación
flask shell

# Ver todos los comandos disponibles
flask --help

# Ver ayuda de un comando específico
flask run --help

En Flask 3.x, ya no es necesario configurar FLASK_APP ni FLASK_DEBUG como variables de entorno. En su lugar, se usa la opción --app y el flag --debug directamente en la línea de comandos:

# Especificar el módulo de la aplicación e iniciar en modo debug
flask --app app run --debug

# Si el archivo se llama app.py o wsgi.py, Flask lo detecta automáticamente
flask run --debug

Con python-dotenv instalado, Flask carga automáticamente el archivo .env para variables de configuración propias de la aplicación:

pip install python-dotenv
# .env
SECRET_KEY=mi-clave-secreta-desarrollo
DATABASE_URL=sqlite:///desarrollo.db

Comandos simples con @app.cli.command()

El decorador @app.cli.command() registra una función Python como un subcomando de la CLI de Flask:

# app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)

@app.cli.command('inicializar-db')
def inicializar_db():
    """Crea todas las tablas de la base de datos."""
    db.create_all()
    click.echo('Base de datos inicializada correctamente.')

@app.cli.command('limpiar-db')
def limpiar_db():
    """Elimina y recrea todas las tablas (¡CUIDADO: borra todos los datos!)."""
    import click
    if click.confirm('¿Estás seguro? Esto borrará TODOS los datos.'):
        db.drop_all()
        db.create_all()
        click.echo('Base de datos limpiada y recreada.')
    else:
        click.echo('Operación cancelada.')

Para ejecutar estos comandos:

flask inicializar-db
flask limpiar-db

Usar Click directamente para mayor flexibilidad

Flask CLI está construido sobre Click, por lo que puedes usar toda la potencia de Click para crear comandos con argumentos, opciones y validaciones:

import click
from flask import Flask

app = Flask(__name__)

@app.cli.command('crear-usuario')
@click.argument('email')
@click.option('--nombre', '-n', required=True, help='Nombre del usuario')
@click.option('--rol', '-r', default='usuario',
              type=click.Choice(['usuario', 'admin', 'moderador']),
              help='Rol del usuario (por defecto: usuario)')
@click.option('--password', '-p', prompt=True, hide_input=True,
              confirmation_prompt=True, help='Contraseña del usuario')
def crear_usuario(email, nombre, rol, password):
    """
    Crea un nuevo usuario en la aplicación.

    EMAIL: dirección de correo electrónico del nuevo usuario.
    """
    from app.models import Usuario
    from app.extensions import db
    from werkzeug.security import generate_password_hash

    # Verificar que el email no existe
    existe = db.session.execute(
        db.select(Usuario).filter_by(email=email)
    ).scalar_one_or_none()
    if existe:
        click.echo(click.style(
            f'Error: Ya existe un usuario con el email {email}',
            fg='red'
        ))
        return

    usuario = Usuario(
        email=email,
        nombre=nombre,
        rol=rol,
        password_hash=generate_password_hash(password)
    )
    db.session.add(usuario)
    db.session.commit()

    click.echo(click.style(
        f'Usuario {nombre} ({email}) creado con rol {rol}.',
        fg='green'
    ))

Uso desde la terminal:

# Con opciones explícitas
flask crear-usuario admin@empresa.com --nombre "Admin Principal" --rol admin

# Con prompt interactivo para la contraseña
flask crear-usuario usuario@ejemplo.com -n "Juan García"

Comandos CLI en Flask

Grupos de comandos

Para organizar múltiples comandos relacionados, usa grupos de comandos de Click:

import click
from flask import Flask

app = Flask(__name__)

@app.cli.group()
def db():
    """Comandos de gestión de base de datos."""
    pass

@db.command('init')
def db_init():
    """Inicializa la base de datos."""
    from app.extensions import db as database
    database.create_all()
    click.echo('Base de datos inicializada.')

@db.command('seed')
@click.option('--cantidad', default=10, help='Número de registros a crear')
def db_seed(cantidad):
    """Pobla la base de datos con datos de prueba."""
    from app.models import Producto
    from app.extensions import db as database
    import random

    categorias = ['electronica', 'ropa', 'hogar', 'deportes']

    for i in range(cantidad):
        producto = Producto(
            nombre=f'Producto {i + 1}',
            precio=round(random.uniform(9.99, 999.99), 2),
            stock=random.randint(0, 100),
            categoria=random.choice(categorias)
        )
        database.session.add(producto)

    database.session.commit()
    click.echo(f'{cantidad} productos de prueba creados.')

@db.command('status')
def db_status():
    """Muestra información sobre el estado de la base de datos."""
    from app.models import Usuario, Producto
    usuarios_count = db.session.execute(
        db.select(db.func.count()).select_from(Usuario)
    ).scalar()
    productos_count = db.session.execute(
        db.select(db.func.count()).select_from(Producto)
    ).scalar()
    click.echo(f'Usuarios: {usuarios_count}')
    click.echo(f'Productos: {productos_count}')

Uso:

flask db init
flask db seed --cantidad 50
flask db status

Comandos en Blueprints

Los Blueprints también pueden registrar sus propios comandos CLI, lo que permite encapsular la lógica administrativa junto al módulo:

# blueprints/productos/__init__.py
from flask import Blueprint
import click

productos_bp = Blueprint('productos', __name__, url_prefix='/productos')

@productos_bp.cli.command('importar')
@click.argument('archivo', type=click.Path(exists=True))
@click.option('--formato', default='csv',
              type=click.Choice(['csv', 'json']),
              help='Formato del archivo de importación')
def importar_productos(archivo, formato):
    """Importa productos desde un archivo CSV o JSON."""
    from app.extensions import db
    from app.models import Producto
    import csv
    import json

    if formato == 'csv':
        with open(archivo, newline='', encoding='utf-8') as f:
            lector = csv.DictReader(f)
            productos = list(lector)
    else:
        with open(archivo, encoding='utf-8') as f:
            productos = json.load(f)

    contador = 0
    for datos in productos:
        producto = Producto(
            nombre=datos['nombre'],
            precio=float(datos['precio']),
            stock=int(datos.get('stock', 0))
        )
        db.session.add(producto)
        contador += 1

    db.session.commit()
    click.echo(f'{contador} productos importados correctamente.')

@productos_bp.cli.command('exportar')
@click.argument('archivo')
def exportar_productos(archivo):
    """Exporta todos los productos a un archivo JSON."""
    from app.models import Producto
    import json

    productos = db.session.execute(db.select(Producto)).scalars().all()
    datos = [p.to_dict() for p in productos]

    with open(archivo, 'w', encoding='utf-8') as f:
        json.dump(datos, f, ensure_ascii=False, indent=2)

    click.echo(f'{len(datos)} productos exportados a {archivo}')

Uso con el Blueprint registrado:

flask productos importar datos/productos.csv --formato csv
flask productos exportar backup_productos.json

El comando flask shell

El comando flask shell abre una consola Python interactiva con el contexto de la aplicación ya cargado. Puedes personalizar qué objetos están disponibles:

# app.py
from flask import Flask
from app.models import Usuario, Producto
from app.extensions import db

app = Flask(__name__)

@app.shell_context_processor
def make_shell_context():
    """Define los objetos disponibles en flask shell."""
    return {
        'db': db,
        'Usuario': Usuario,
        'Producto': Producto,
    }

Con esta configuración, al ejecutar flask shell tendrás acceso directo a db, Usuario y Producto:

# Dentro de flask shell
>>> db.session.execute(db.select(db.func.count()).select_from(Usuario)).scalar()
5
>>> p = Producto(nombre='Test', precio=9.99, stock=10)
>>> db.session.add(p)
>>> db.session.commit()
>>> db.session.execute(db.select(Producto).filter_by(nombre='Test')).scalar_one_or_none()
<Producto Test>

Comandos de mantenimiento y tareas programadas

Los comandos CLI de Flask son ideales para tareas de mantenimiento que se ejecutan periódicamente mediante cron jobs o schedulers:

@app.cli.command('limpiar-sesiones')
@click.option('--dias', default=30, help='Sesiones más antiguas que N días')
def limpiar_sesiones(dias):
    """Elimina sesiones expiradas de la base de datos."""
    from datetime import datetime, timedelta
    from app.models import Sesion
    from app.extensions import db

    fecha_limite = datetime.utcnow() - timedelta(days=dias)
    count = db.session.execute(
        db.delete(Sesion).where(Sesion.ultima_actividad < fecha_limite)
    ).rowcount
    db.session.commit()

    click.echo(f'Eliminadas {count} sesiones expiradas (anteriores a {dias} días).')

@app.cli.command('enviar-resumen')
@click.option('--destinatario', '-d', multiple=True, required=True)
def enviar_resumen(destinatario):
    """Envía el resumen diario de actividad por email."""
    from app.servicios.email import enviar_email
    from app.servicios.reportes import generar_resumen_diario

    resumen = generar_resumen_diario()

    for email in destinatario:
        enviar_email(
            destinatario=email,
            asunto='Resumen diario de actividad',
            cuerpo=resumen
        )
        click.echo(f'Resumen enviado a {email}')

Para ejecutar como cron job en Linux:

# Ejecutar cada día a las 6:00 AM
0 6 * * * /ruta/venv/bin/flask --app app enviar-resumen -d admin@empresa.com

Los comandos CLI de Flask permiten automatizar tareas administrativas manteniendo el código dentro del ecosistema de la aplicación, con acceso completo al contexto, modelos y servicios.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Flask

Documentación oficial de Flask
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Flask es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Flask

Explora más contenido relacionado con Flask y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comprender el sistema CLI de Flask basado en Click. Crear comandos personalizados con @app.cli.command() y grupos de comandos. Añadir comandos CLI a Blueprints específicos. Usar parámetros, opciones y argumentos en comandos Flask. Ejecutar comandos con flask run, flask shell y comandos propios.