Django自定义命令必须继承django.core.management.BaseCommand,置于management/commands/目录下,实现handle()和add_arguments()方法,支持参数解析、事务控制、标准输出及可测试性。

直接用 django.core.management.BaseCommand 就能写出可复用、可测试、可传参的后台脚手架,不需要额外框架或魔改。
继承 BaseCommand 是唯一正统入口
Django 的命令机制只认这个基类,其他方式(比如直接写独立脚本)会丢失 settings 加载、数据库连接、信号注册等关键上下文。
- 必须放在
management/commands/目录下,文件名即命令名(如mytask.py→python manage.py mytask) -
handle(self, *args, **options)是主逻辑入口,args对应位置参数,options来自add_arguments()定义的命名参数 - 别在
__init__或类属性里做业务初始化——Django 命令实例每次执行都新建,但模块级代码可能被多次导入
add_arguments(self, parser) 决定脚手架是否好用
参数设计直接影响运维体验和自动化集成能力。别只用 --dry-run 这种万金油选项。
- 用
parser.add_argument('--batch-size', type=int, default=100)控制资源消耗,避免大表操作 OOM - 对必填项加
required=True,比在handle里if not options['xxx']更早暴露问题 - 支持
nargs='+'接收多个值(如--user-id 123 456 789),比用逗号分隔字符串更健壮 - 避免用
action='store_true'表达“启用某功能”,而要用--mode=sync|async这类显式枚举,防止语义模糊
数据库操作必须考虑事务与日志粒度
后台脚本不是 Web 请求,没有中间件自动包事务,也不受 ATOMIC_REQUESTS 影响。
立即学习“Python免费学习笔记(深入)”;
- 批量更新/删除务必手动加
transaction.atomic(),否则中途失败会导致数据不一致 - 用
self.stdout.write()输出进度,而不是print()——它兼容--verbosity级别控制,且能被管道捕获 - 敏感操作(如清空表)建议默认禁用,强制要求
--force参数,且首次运行时用input('确认删除?y/N')交互确认 - 别在循环里反复调用
.save(),改用bulk_create()或bulk_update(),性能差一个数量级
测试脚手架比写它还重要
没人会手动跑十遍命令验证逻辑,但没测试的脚手架上线就是定时炸弹。
- 用
call_command('mytask', '--batch-size=50')在 TestCase 中触发,检查返回值、DB 状态、输出内容 - 对耗时操作(如调外部 API),用
unittest.mock.patch替换真实调用,避免测试不稳定 - 错误路径必须覆盖:传错参数、DB 连接失败、权限不足(如无 delete 权限时删记录)
- 别测 “命令是否注册成功” 这种 Django 自带保障的点,专注你写的业务逻辑
最常被忽略的是信号监听——脚手架里调 .save() 不会触发 post_save,除非显式传 update_fields 或用 dispatch_uid 避免重复注册;还有就是本地开发时忘了 DEBUG=False 下的静态文件收集行为差异。


















