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

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

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

创建文件夹布局

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

 app01/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            __init__.py
            _private.py # 以下划线开头文件不能用作管理命令
            my_commands.py # 这个就是自定义的管理命令脚本,文件名即为命令名
    tests.py
    views.py

注意:

  • management和commands每个目录下都必须有个__init__.py空文件,表明这是一个python包。另外以下划线开头的文件名不能用作管理命令脚本。
  • management/commands目录可以位于任何一个app的目录下,Django都能找到它。
  • 一般建议每个python脚本文件对应一条管理命令。

编写命令代码

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

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

注意:当你使用管理命令并希望在控制台输出指定信息时,你应该使用self.stdout和self.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 BaseCommand
 
 class 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.py
 
 import time
 from django.db import connections
 from django.db.utils import OperationalError
 from django.core.management import BaseCommand
 
 
 class Command(BaseCommand):
     help = 'Run data migrations until db is available.'
 
     def handle(self, *args, **options):
         self.stdout.write('Waiting for database...')
         db_conn = None
         while 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, datetime
 from django.core.mail import mail_admins
 from django.core.management import BaseCommand
 from django.utils import timezone
 from django.contrib.auth import get_user_model
 
 User = 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目录下新建task
 from celery import shared_task
 from django.core.management import call_command
 
 @shared_task
 def mail_admin():
     call_command("mail_admin", )

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

以上就是django如何自定义manage.py管理命令的详细内容,更多关于django 自定义manage.py管理命令的资料请关注程序员的世界其它相关文章!

django如何自定义manage.py管理命令的更多相关文章

  1. pandas merge报错的解决方案

    pandas 做merge的时候报这个错:df22 = pd.merge(df1,df2,left_on='company_name',right_on = 'name',how='left') Process finished with exit code 137查了一下原因是:两个表太大了,可能......

  2. pandas快速处理Excel,替换Nan,转字典的操作

    pandas读取Excelimport pandas as pd# 参数1:文件路径,参数2:sheet名pf = pd.read_excel(path, sheet_name='sheet1')删除指定列# 通过列名删除指定列pf.drop(['序号', '替代', '签名'], axis=1, ......

  3. python实现自幂数的示例代码

    1、什么是自幂数?前文介绍过 python 实现水仙花数,其实水仙花数为自幂数的一种,即,3位自幂数。自幂数是指一个 n 位数,它的每个位上的数字的 n 次幂之和等于它本身。(例如:当n为3时,有1^3 + 5^3 + 3^3 = 153,153即是n为3时的一个自幂数)自幂数-百度百科2、自幂......

  4. 如何用python插入声明

    想必写毕设的时候,大家都会遇到一个问题,那就是得在明评版的论文里面插入一个独创性声明。就因为这个事情,我折腾了好久,各种在线网站都试过了,然而基本都需要充值或者会员啥的。(小声嚷嚷:“万恶的资本”)害~一不做二不休,我干脆自己写个小工具好了。一、代码分析利用PyPDF2库便可轻松地对PDF文件进行处......

  5. Python+Django+Eclipse 在Windows下快速开发自己的网站

    一、配置开发环境我的开发环境是:Python3.3.2 + Django1.5.2 + Eclipse1、安装Python下载地址:http://www.python.org/getit/安装完成后为了方便可以配置下环境变量:2、安装Django—Python下用于开发网站的比较流行的web框架下载......

  6. Python 学习笔记(1)

    Mac下载安装Pythonmac 系统自带有python 。但就最新的mac系统而言,它自带的python版本为2.*版本。虽然不影响对于老python项目的运行,但3.*版本中很多语法都发生了改变,为了方便后面的学习和使用还是最新版本会好一点1. 查看mac系统自带的pythonmac自带pyth......

  7. 如何使用Python进行PDF图片识别OCR

    使用场景 使用图片识别可以快速提取图片中的信息,方便高效。 Python并不能直接对PDF进行识别,所以如果是识别PDF的话,需要先将PDF转化为图片,然后再进行识别。 必备工具 Python 可以安装3.7及以上版本 tesseract-ocr......

  8. [Python] Pandas 对数据进行查找、替换、筛选、排序、重复值和缺失值处理

    如何使用pandas模块中的函数对DataFrame中的数据进行查找和替换目录1. 数据文件2. 读数据3. 查找数据4. 替换数据4.1 一对一替换4.2 多对一替换4.3 多对多替换5. 插入数据6. 删除数据6.1 删除列6.2 删除行7. 处理缺失值7.1 数据准备7.2 查看缺失值7.3 ......

  9. python中编写函数并调用的知识点总结

    能够调用自己编写的函数,这在很多开发语言中,都会用到一个叫做mian的主函数,这个函数一般都是程序的入口,当程序启动时,首先执行这个函数。在Python中,main函数的主要作用就是你写的模块既可以导入到别的模块中用,也可以在模块本身执行使用。下面就来了解具体使用操作吧。编写简单的函数并调用:def......

  10. Python 打印自己设计的字体的实例讲解

    通过对 26 个字母的设定,设置自己要输出的字体。name = "RUNOOB"# 接收用户输入# name = input("输入你的名字: \n\n") lngth = len(name) l = "" for x in range(0......

随机推荐

  1. Linux磁盘空间释放问题整理

    IDC里的一台服务器的/分区使用率爆满了!已达到100%!经查看发现有个文件过大(80G),于是在跟有关同事确认后rm -f果断删除该文件。但是发现删除该文件后,/分区的磁盘空间压根没有释放出来,使用率还是100%!这是为什么呢??[root@linux-node1 ~]# df -hFilesys......

  2. mybatis plus代码生成工具的实现代码

    前言:原本想使用AutoGenerator 是 MyBatis-Plus 的官方代码生成器 ,尝试了一下,竟然报错,原因可能是MyBatis-Plus和mybatis-plus-generator 的版本不一致,因为我用的MyBatis-Plus 的版本是3.42 ,但是mybatis-plus......

  3. 封装Vue纵向表头左右结构的table表格

    我们前端开发人员在使用表格的过程中,大概率碰到的都是表格头部在表格的最上边,然后呈一行展示,紧接着就是表格的每一行的每一个单元格来展示具体内容的场景,很少会遇到表格的头部呈纵向一行展示,也就是说表格的头部在左边,具体的内容在右边(也可以说是左右结构)这种使用场景,而且有时候的场景可能会是纵向表头有两......

  4. python 窃取摄像头照片的实现示例

    python窃取摄像头照片源码+获取授权码方法+py打包成exe教你用python做一个属于自己的窃取摄像头照片的软件。需要安装python3.5以上版本,在官网下载即可。然后安装库opencv-python,安装方式为打开终端输入命令行。可以在使用pip的时候加参数-i http://pypi.t......

  5. Tomcat正常访问localhost报404问题解决

    今天在配置Tomcat访问项目主页的时候发现报404错误,开始以为是我的项目xml配置有问题,结果调了半天也不对,后来发现居然访问localhost:8080也报404,这个问题就严重了,于是开始了漫长的调试,终于成功!记录下最终解决方案,以供未来再遇到相似问题,有迹可查。 ......

  6. asp中将字符串转数字的函数小结

    常常因为一些小地放的失误导致整个程序瘫痪.今天就遇到了.一个商城系统.因为计算整得我改了N久在此记下这些数据转换的函数cstr()转换成字符串cint()转换成数字先用isnumberic函数判断clng()转换为 Long 类型的 Variant(推荐使用clng代替cint,尤其是文章id等可能......

  7. Java多线程-锁的区别与使用

    目录锁类型可中断锁公平锁/非公平锁可重入锁独享锁/共享锁互斥锁/读写锁乐观锁/悲观锁分段锁偏向锁/轻量级锁/重量级锁自旋锁Synchronized与Static Synchronized举例Lock定义四种获取Lock的方法区别lock()tryLock()tryLock(long time, Ti......

  8. js中获得当前时间是年份和月份

    js中获得当前时间是年份和月份,形如:201208 //获取完整的日期 var date=new Date; var year=date.getFullYear(); var month=date.getMonth()+1; month ......

  9. 详解React 父组件和子组件的数据传输

    在学习 React 框架组件间数据传输知识点前,我们需要先明确几点使用原则。React的组件间通讯是单向的。数据必须是由父级传到子级或者子级传递给父级层层传递。如果要给兄弟级的组件传递数据,那么就要先传递给公共的父级而后在传递给你要传递到的组件位置。这种非父子关系的组件间传递数据,不推荐使用这种层层......

  10. Java 并发工具类 CountDownLatch、CyclicBarrier、Semaphore、Exchanger

    本文部分摘自《Java 并发编程的艺术》CountDownLatchCountDownLatch 允许一个或多个线程等待其他线程完成操作。假设现有一个需求:我们需要解析一个 Excel 里多个 sheet 的数据,此时可以考虑使用多线程,每个线程解析一个 sheet 的数据,等到所有的 sheet ......