每次在启动Django服务之前,我们都会在终端运行python manage.py xxx的管理命令。其实我们还可以自定义管理命令,这对于执行独立的脚本或任务非常有用,比如清除缓存、导出用户邮件清单或发送邮件等等。

自定义的管理命令不仅可以通过manage.py运行,还可以通过Linux或Celery的crontab服务将其设成定时任务。本文主要讲解如何自定义Django-admin命令,并提供一些演示案例。

自定义Django-admin命令一共分三步:创建文件夹布局、编写命令代码和测试使用。

创建文件夹布局

自定义的Django-admin管理命令本质上是一个python脚本文件,它的存放路径必须遵循一定的规范,一般位于app/management/commands目录。整个文件夹的布局如下所示:

 app01/__init__.pymodels.pymanagement/__init__.pycommands/__init__.py_private.py # 以下划线开头文件不能用作管理命令my_commands.py # 这个就是自定义的管理命令脚本,文件名即为命令名tests.pyviews.py

注意

  • managementcommands每个目录下都必须有个__init__.py空文件,表明这是一个python包。另外以下划线开头的文件名不能用作管理命令脚本。

  • management/commands目录可以位于任何一个app的目录下,Django都能找到它。

  • 一般建议每个python脚本文件对应一条管理命令。

编写命令代码

每一个自定义的管理命令本质是一个Command类, 它继承了Django的Basecommand或其子类, 主要通过重写handle()方法实现自己的业务逻辑代码,而add_arguments()则用于帮助处理命令行的参数,如果运行命令时不需要额外参数,可以不写这个方法。

 from django.core.management.base import BaseCommandclass Command(BaseCommand):# 帮助文本, 一般备注命令的用途及如何使用。help = 'Some help texts'# 处理命令行参数,可选def add_arguments(self, parser):pass# 核心业务逻辑def handle(self, *args, **options):pass

我们现在来看一个最简单的例子,希望定义一个名为hello_world的命令。这样当我们运行python manage.py hello_world命令时,控制台会打印出Hello World!字样。在app/management/commands目录下新建hello_world.py, 添加如下代码:

 from django.core.management.base import BaseCommandclass Command(BaseCommand):# 帮助文本, 一般备注命令的用途及如何使用。help = "Print Hello World!"# 核心业务逻辑def handle(self, *args, **options):self.stdout.write('Hello World!')

注意:当你使用管理命令并希望在控制台输出指定信息时,你应该使用self.stdoutself.stderr方法,而不能直接使用python的print方法。另外,你不需要在消息的末尾加上换行符,它将被自动添加。

此时当你进入项目文件夹运行python manage.py hello_world命令时,你将得到如下输出结果:

现在我们来增加点难度,来通过命令行给hello_world命令传递一个name参数,以实现运行python manage.py helloworld John命令时 打印出Hello World! John

现在修改我们的hello_world.py, 添加add_arguments方法,该方法的作用是给自定义的handle方法添加1个或多个参数。

 from django.core.management.base import BaseCommandclass Command(BaseCommand):# 帮助文本, 一般备注命令的用途及如何使用。help = "Print Hello World!"# 给命令添加一个名为name的参数def add_arguments(self, parser):parser.add_argument('name')# 核心业务逻辑,通过options字典接收name参数值,拼接字符串后输出def handle(self, *args, **options):msg = 'Hello World ! '+ options['name']self.stdout.write(msg)

此时当你再次运行python manage.py hello_world John命令时,你将得到如下输出结果:

如果你直接运行命令而不携带参数,将会报错,如下所示:

实际应用场景

前面的案例过于简单,我们现在来看两个自定义管理命令的实际应用案例。

案例1:检查数据库连接是否已就绪

无论你使用常规方式还是Docker在生产环境中部署Django项目,你需要确保数据库连接已就绪后才进行数据库迁移(migrate)的命令(Docker-compose的depends选项并不能确保这点),否则Django应用程序会出现报错。

这时你可以自定义一个wait_for_db的命令,如下所示:

 # app/management/commands/wait_for_db.pyimport timefrom django.db import connectionsfrom django.db.utils import OperationalErrorfrom django.core.management import BaseCommandclass Command(BaseCommand):help = 'Run data migrations until db is available.'def handle(self, *args, **options):self.stdout.write('Waiting for database...')db_conn = Nonewhile not db_conn:try:# 尝试连接db_conn = connections['default']except OperationalError:# 连接失败,就等待1秒钟self.stdout.write('Database unavailable, waiting 1 second...')time.sleep(1)self.stdout.write(self.style.SUCCESS('Database available!'))

定义好这个命令后每次在运行python manage.py migrate命令前先运行python manage.py wait_for_db即可。

案例2:周期性发送邮件

如果你是网站管理员,你肯定希望知道每天有多少新用户已注册,这时你可以自定义一条mail_admin的管理命令,将每天新注册用户数量以邮件形式发给自己,如下所示:

 # app/management/commands/mail_admin.py#-*- coding:utf-8 -*-from datetime import timedelta, time, datetimefrom django.core.mail import mail_adminsfrom django.core.management import BaseCommandfrom django.utils import timezonefrom django.contrib.auth import get_user_modelUser = get_user_model()today = timezone.now()yesterday = today - timedelta(1)class Command(BaseCommand):help = "Send The Daily Count of New Users to Admins"def handle(self, *args, **options):# 获取过去一天注册用户数量user_count =User.objects.filter(date_joined__range=(yesterday, today)).count()# 当注册用户数量多余1个,才发送邮件给管理员if user_count >= 1:message = "You have got {} user(s) in the past 24 hours".format(user_count)subject = (f"New user count for {today.strftime('%Y-%m-%d')}: {user_count}")mail_admins(subject=subject, message=message, html_message=None)self.stdout.write("E-mail was sent.")else:self.stdout.write("No new users today.")

如果你在终端运行python manage.py mail_admin命令,你将得到如下输出结果:

注意:真正发送邮件成功需要设置Email后台及管理员,测试环境下可以使用如下简单配置:

 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"DEFAULT_FROM_EMAIL = "noreply@example.com"ADMINS = [("大江狗", "yunbo.shi@example.com"), ]

但是如果每天都要进入终端运行这个命令实在太麻烦了,我们完全可以使用Linux的crontab服务或Celery-Beat将其设成周期性定时任务task,这时只需要调用Django的call_command方法即可。

 # app/tasks.py, 可以任一app目录下新建taskfrom celery import shared_taskfrom django.core.management import call_command@shared_taskdef mail_admin():call_command("mail_admin", )

关于Django项目中如何使用Celery执行异步和周期性任务,请参加下篇Django进阶-异步和周期任务篇。

推荐阅读

Django进阶(1): admin后台高级玩法(多图)

更多原创Django高级进阶教程见大江狗技术博客(https://pythondjango.cn或关注我们的微信公众号:

Django进阶: 如何自定义manage.py管理命令相关推荐

  1. python 运行cmd命令失败怎么办_python manage.py runserver命令在cmd命令框中可以正确执行,但是在pycharm的终端中运行就失败了!...

    源自:2-2 初始Django项目 python manage.py runserver命令在cmd命令框中可以正确执行,但是在pycharm的终端中运行就失败了! (venv) E:\python\ ...

  2. 使用Django在执行python manage.py startapp myApp创建应用

    使用Django在执行python manage.py startapp myApp创建应用 出现很长的报错信息并且最后两行提示: raise ImproperlyConfigured('mysqlc ...

  3. 解决django运行manage.py runscript命令时报错Try running with a higher verbosity level like: -v2 or -v3

    解决方法 查看命令是否书写正确:比如脚本名称为 my_script.py 时: python3 manage.py runscript my_script my_script.py文件中需要有入口函数 ...

  4. django源码阅读 manage.py文件

    Django源码阅读之manager.py文件阅读 我们知道,我们运行一个django项目的时候,需要进入项目的根目录,然后输入命令,python manage.py runserver,这样,我们就 ...

  5. manage.py 常用命令

    manage.py是Django项目中自动生成的一个用于管理项目的脚本文件,通过python命令执行.manage.py接受的是Django提供的内置命令 1.创建数据库表 或 更改数据库表或字段 # ...

  6. 【Django】执行python manage.py makemigrations报错的解决方案

    我是一个甜甜的大橙子

  7. Flask 自定义命令 类似于django的manage.py

    Flask-Script 扩展 文档: https://flask-script.readthedocs.io/en/latest/ 这个模块的作用可以让我们通过终端来控制flask项目的运行,类似于 ...

  8. django manage.py扩展自定义命令

    # django manage.py扩展自定义命令 环境: mac django1.10.3 在实际的项目开发过程中,我们可能要执行某脚本初始化数据库,可能要启动多个服务,比如启动celery,red ...

  9. Django常用命令django-admin.py和manage.py用法详解

    学习Django你需要了解常见命令的使用,比如创建项目,创建应用,创建超级用户,数据表创建及更新,启动服务器等.这些命令都包含在django-admin.py和manage.py里.除此以外manag ...

最新文章

  1. 【电子基础】模拟电路问答
  2. Python爬虫入门教程 33-100 《海王》评论数据抓取 scrapy
  3. 在mac上配置cocos2d-x开发环境
  4. c++矩阵类_Python线性代数学习笔记——矩阵的基本运算和基本性质,实现矩阵的基本运算...
  5. php的socket通信
  6. 图论--LCA--Tarjan(离线)
  7. 工作按钮(216):点击按钮报错--bug修复--直接写接口里面
  8. asp.net实现无刷新,无须AJAX
  9. JS/PHP中,数组与字符串的转换,这次总算是记住了
  10. 全向轮机器人逆时针自旋运动分析
  11. 极点五笔常用操作及快捷键功能描述(v6.5)
  12. 解决linux下 firefox 浏览器 视频无法播放问题
  13. 企业财务报表分析【3】
  14. 硬件探索——数字钟的设计与制作
  15. C++中函数返回引用
  16. 电脑连不上ishanghai_ishanghai用电脑肿么连网
  17. 加拿大留学计算机专业好移民吗,加拿大最适合留学转移民的热门专业——计算机科学及信息技术...
  18. stata命令汇总_Stata常用命令应知应会(500条)
  19. 爬虫实战——爬取小说《从你的全世界路过》
  20. 【FPGA】调用IP核实现心形波

热门文章

  1. C++通过ATL访问Excel时 使用COleSafeArray批量读写数据及公式
  2. 加入ZigBee联盟,共画物联网的未来
  3. Mac如何快速打开library(资源库文件夹)
  4. 【书签百宝箱】技术宅收藏积累的所有实用性网站、技术教程、工具网站大全(长期更新...)
  5. 电商 静态页面(详细讲解)
  6. File类中isFile与exists的区别
  7. 天津大学计算机专业复试,天津大学计算机应用复试全过程及感受
  8. PayPal BrainTree 是怎么运作的
  9. 【JS】去NM的文字验证码
  10. 华为2021软件精英挑战赛思路分享