Flask 笔记十五:自定义 Flask CLI 命令
上一篇我们用 Gunicorn 把应用跑上了服务器。部署时还会反复做几件事第一次flask db upgrade建表创建管理员账号导入测试数据、同步权限……每次都要记一长串 shell容易漏步骤。Flask 自带 CLI 扩展可以把这些操作收成flask xxx一条命令。这一篇做一件事学会写自己的 Flask CLI 命令并理解flask db是怎么挂上去的。例子仍是通用的Note备忘录项目不涉及任何真实业务。1. 学完后你能做什么用app.cli.command()注册命令写flask init-db、flask create-admin给命令加 参数、选项Click 语法把 CLI 代码 拆到单独文件 再注册知道命令里为什么要app.app_context()在 第十四篇部署流程 里固定调用这些命令2.flask db从哪来第七篇装过 Flask-Migrate 后终端里多了flask db initflask db migrate -m add is_pinnedflask db upgrade这不是 Flask 内置的是 Migrate 扩展注册进 CLI 的。你写的自定义命令走的也是同一条路挂到app.cli上。flask db upgrade ← Flask-Migrate 注册的flask create-admin ← 你自己注册的│▼app.cliClick 命令组│▼你的 Python 函数3. 最小示例hello 命令app/__init__.py末尾或单独app/cli.pyimport clickfrom app import appapp.cli.command(hello)def hello_command():打个招呼docstring 会出现在 flask --help 里。click.echo(Hello from Flask CLI!)运行export FLASK_APPappflask hello# Hello from Flask CLI!查看帮助flask --helpflask hello --help习惯开发、服务器上都设FLASK_APPapp或manage.py里from app import app时用flask --app app。4. 实用命令一init-db有时新机器第一次部署想 确保表存在Migrate 仍推荐做主流程这里演示 CLI 写法app/cli_commands.pyimport clickfrom app import app, dbapp.cli.command(init-db)def init_db_command():创建所有表开发/救急用生产优先 flask db upgrade。click.echo(正在 create_all …)db.create_all()click.echo(完成。)app/__init__.py里导入触发注册import app.cli_commands # noqa: F401flask init-db生产环境 仍应以flask db upgrade为主create_all()不会帮你做列变更和第七篇讲的一致。5. 实用命令二create-admin第十一篇有 admin 后台首次部署要有一个管理员import clickfrom werkzeug.security import generate_password_hashfrom app import app, dbfrom app.models import User # 假设 User 表name, pwd, is_adminapp.cli.command(create-admin)click.argument(username)click.option(--password, defaultNone, help不传则交互式输入)def create_admin_command(username, password):创建或重置管理员账号。from app.models import Userusername (username or ).strip()if not username:raise click.ClickException(用户名不能为空)if not password:password click.prompt(密码, hide_inputTrue, confirmation_promptTrue)row User.query.filter_by(nameusername).first()if row is None:row User(nameusername, is_adminTrue)click.echo(将新建管理员%s % username)else:click.echo(账号已存在将重置密码%s % username)row.pwd generate_password_hash(password)row.is_admin Truedb.session.add(row)db.session.commit()click.echo(完成。)用法flask create-admin liousa# 提示输入密码flask create-admin liousa --password YourStrongPass123出错时用raise click.ClickException(原因)终端会红色提示并以非 0 退出码结束适合脚本判断。6. 参数与选项Click 速查装饰器含义示例click.argument(name)positional positional 必填位置参数flask cmd aliceclick.option(--dry-run, is_flagTrue)开关出现即为 Trueflask sync --dry-runclick.option(--count, default10, typeint)可选参数flask seed --count 5click.prompt(密码, hide_inputTrue)交互输入创建用户带--dry-run的同步示例app.cli.command(sync-permissions)click.option(--dry-run, is_flagTrue, help只打印不写库)def sync_permissions_command(dry_run):把路由表里的后台 URL 同步进权限表。would_add [admin.note_list, admin.note_export] # 实际应扫描 viewsfor name in would_add:click.echo([dry-run] 将新增 %s % name if dry_run else 新增 %s % name)if dry_run:click.echo(dry-run 结束未写库。)return# db.session.commit() …click.echo(已写入。)7. 命令里访问数据库app_contextFlask 请求外终端、定时任务、CLI没有自动application context。若直接User.query...可能报错。两种写法写法 A命令函数里包一层简单项目够用app.cli.command(count-notes)def count_notes_command():with app.app_context():from app.models import Noten Note.query.count()click.echo(共 %s 条备忘录。 % n)写法 B把逻辑抽到services/CLI 只负责调推荐和第十篇一致# app/note_service.pydef count_all_notes():from app.models import Notereturn Note.query.count()# app/cli_commands.pyapp.cli.command(count-notes)def count_notes_command():with app.app_context():from app.note_service import count_all_notesclick.echo(共 %s 条。 % count_all_notes())8. CLI 拆文件 register_cli项目变大后别把几十条命令全堆在__init__.py。可学 Migrate / 真实项目 的注册方式app/cli_commands.pyimport clickdef register_cli(app):app.cli.command(hello)def hello_command():click.echo(Hello!)app.cli.command(create-admin)click.argument(username)def create_admin_command(username):...app/__init__.pyfrom app.cli_commands import register_cliregister_cli(app)一个文件register_cli(app)里面注册多条命令边界清晰。9. 和部署流程怎么接接第十四篇新服务器 最小闭环 可以写成cd /var/www/myappsource .venv/bin/activateset -a source /etc/myapp.env set aexport FLASK_APPapppip install -r requirements.txtflask db upgrade # 表结构flask create-admin admin # 首个管理员仅首次sudo systemctl restart myapp写进 部署文档 或 CI 脚本比口头记命令可靠。10.flask runvsmanage.pyvs CLI入口干什么python manage.py开发服务器app.run()flask run同上需FLASK_APPflask db upgradeMigrateflask create-admin你写的运维命令gunicorn app:app生产 HTTPCLI 命令和 Gunicorn 互不替代Gunicorn 跑 WebCLI 跑 一次性/运维任务。11. 流程示意运维 / 开发者│▼flask create-admin bob│▼Click 解析参数│▼app.app_context()│▼create_admin 逻辑写 User 表│▼click.echo(完成)app/__init__.py│├── register_cli(app) → hello, create-admin, …├── Migrate(app, db) → flask db *└── 其它 register_cli → 各业务模块自带命令12. 新手常踩的 6 个坑没设FLASK_APP— 报找不到应用设export FLASK_APPapp或flask --app app。CLI 里直接 query 不报 context — 包with app.app_context():。循环 import — CLI 文件里from app import app时把注册放在register_cli(app)里由__init__.py最后调用。密码写进命令行历史 — 用click.prompt(hide_inputTrue)别flask xxx --password 123上生产。生产滥用init-db/create_all— 表结构变更走 Migrate。命令无 docstring — 补一行说明三个月后的你会感谢现在。13. 小结记住五件事app.cli.command(名字)注册命令Click 管参数argument、option、prompt数据库操作包app.app_context()逻辑放 serviceCLI 只当入口和第十篇一致部署清单里固定flask db upgrade 你的运维命令十五篇下来开发、API、上线、运维命令这条线就闭合了。