Command类必须继承BaseCommand,因Django命令机制仅识别BaseCommand及其子类;AppCommand是为app过滤设计的抽象基类,不可直接运行,继承错误将导致Unknown command错误。

为什么 Command 类必须继承 BaseCommand 而不是 AppCommand
Django 的自定义命令机制只认 BaseCommand 及其子类;AppCommand 是为按 app 过滤管理命令设计的抽象基类,不能直接运行。继承错会导致 Unknown command 错误,哪怕文件放对位置、名字合法也没用。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有自定义命令脚本都放在
myapp/management/commands/xxx.py,且必须定义一个叫Command的类 - 该类必须继承
django.core.management.BaseCommand - 必须实现
handle(self, *args, **options)方法,逻辑写在这里 - 如果要用
--dry-run或--days=7这类参数,得在add_arguments(self, parser)里注册,不能靠**options碰运气
handle() 里怎么安全删数据,避免误删和锁表
直接写 MyModel.objects.filter(...).delete() 看似简单,但线上跑可能卡住事务、拖慢查询,甚至因外键约束失败而中断。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
queryset.iterator(chunk_size=1000)分批查,再逐批删,避免内存爆掉或长事务 - 删除前加
print(f"Will delete {qs.count()} items")(配合--dry-run判断) - 涉及外键级联时,确认模型的
on_delete行为,别依赖数据库默认值 - 生产环境务必加
transaction.atomic()包裹单次批量删,防止部分成功部分失败
示例片段:
def handle(self, *args, **options):
days = options.get("days", 30)
qs = LogEntry.objects.filter(timestamp__lt=timezone.now() - timedelta(days=days))
if options.get("dry_run"):
self.stdout.write(f"[DRY RUN] Would delete {qs.count()} LogEntry records")
return
for obj in qs.iterator(chunk_size=500):
obj.delete() # 或 qs.delete() 如果不关心 post_delete 信号
如何让命令支持 cron 定期执行又不冲突
多个相同命令同时跑,可能重复删数据、抢锁、写日志混乱——Django 不自带分布式锁,得自己防。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
cache.add("cleanup_log_entry_lock", "1", timeout=3600)做轻量级锁,失败就退出并 log - 别依赖
os.environ或临时文件判断是否在运行,它们跨进程不可靠 - crontab 写成
0 2 * * * cd /path/to/project && python manage.py cleanup_logs --days=30 >> /var/log/django/cleanup.log 2>&1,注意路径和重定向 - 日志里必须打上时间戳和 PID:
self.stdout.write(f"[{datetime.now()}][PID:{os.getpid()}] Started")
manage.py cleanup_logs 执行时报 No module named 'myapp'
这不是命令写错了,是 Django 没法识别你的 app —— 很可能 INSTALLED_APPS 里没加,或者 myapp 目录下缺 __init__.py(尤其用 pyproject.toml + PEP 517 构建时容易漏)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 运行
python manage.py showmigrations,看myapp是否出现在列表里 - 确认
myapp/management/__init__.py和myapp/management/commands/__init__.py都存在(哪怕空) - 检查
manage.py同级有没有settings.py,或者DJANGO_SETTINGS_MODULE是否指向正确模块路径 - 如果用 poetry/virtualenv,确保激活环境后再跑命令,别混用系统 Python
最常被忽略的一点:命令文件名不能用下划线开头(如 _cleanup.py),Django 会跳过加载。必须是合法 Python 标识符,且不以 _ 开头。








