django如何自定义manage.py管理命令
作者:大江狗 发布时间:2023-04-08 01:10:47
目录
创建文件夹布局
编写命令代码
实际应用场景
案例1:检查数据库连接是否已就绪
案例2:周期性发送邮件
每次在启动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进阶-异步和周期任务篇。
来源:https://mp.weixin.qq.com/s/KNn0HjPI8iyTwwNEZ485Cg


猜你喜欢
- 创建 var d=new Date(); 要注意的是在JavaScript中月份的值是从0到11(0表示1月)。 设置日期和时间值 设置日期
- terminal(命令行)作为本地IDE普遍拥有的功能,对项目的git操作以及文件操作有着非常强大的支持。对于WebIDE,在没有web伪终
- # -*- coding: utf-8 -*- import httplibfrom urllib import urlencodeimpo
- 1. 前言数组和矩阵是数值计算的基础元素。目前为止,我们都是使用NumPy的ndarray数据结构来表示数组,这是一种同构的容器,用于存储数
- CookieHTTPHTTP(Hyper Text Transfer Protocol,超文本传输模式)属于无状态协议,在同一个连接中,两个
- python 实现文件查找和某些项输出本文是基于给定一文件(students.txt),查找其中GPA分数最高的 输出,同时输出其对应的姓名
- 世上无难事,只要找到 Homebrew 的正确安装方式。Homebrew 是什么Homebrew是 mac的包管理器,仅需执行相应的命令,就
- 本文实例为大家分享了Python实现GUI学生信息管理系统的具体代码,供大家参考,具体内容如下项目环境: 软件环境: &
- Variant变量一般会将其代表的数据子类型自动转换成合适的数据类型,但有时候,自动转换也会造成一些数据类型不匹配的错误.这时,可使用转换函
- 我们先从一个常见的Python编程错误开始说起,我已经见过非常多的程序员犯过这种错误了:def do_not_raise(user_defi
- 前言日志无论对于程序还是程序员都非常重要,有多重要呢,想要长期在公司健健康康的干下去就得学会阶段性划水,阶段性划水的一大关键的就是干活快过预
- 机器学习可应用在各个方面,本篇将在系统性进入机器学习方向前,初步认识机器学习,利用线性回归预测波士顿房价;原理简介利用线性回归最简单的形式预
- 方法一在 Pillow 中,resize() 方法会强制将图片缩放到指定的大小,可能会导致图片变形或失真;而 thumbnail() 方法则
- MySQL数据库没有增量备份的机制,但它提供了一种主从备份的机制,就是把主数据库的所有的数据同时写到备份数据库中。实现MySQL数据库的热备
- 1 动机greenlet 包是 Stackless 的副产品,其将微线程称为 “tasklet” 。tasklet运
- 1、设置字体、风格代码主题选择Monokai会是彩色的代码。2、配置CI代码提示<1>下载代码提示项目:https://gith
- QMainWindow基本介绍QMainWindow主窗口为用户提供了一个应用程序框架,它有自己的布局,可以在布局中添加控件。窗口类型介绍P
- 目录系列教程一、用户管理1、用户账号2、增加删除账号3、破解管理账号密码二、授权管理1、授权2、查询授权3、收回授权总结系列教程MySQL系
- 本文实例为大家分享了Python实现简单飞机大战的具体代码,供大家参考,具体内容如下功能玩家飞机可以移动,可以发射 * ,敌机随机产生,自由坠
- 借助zookeeper可以实现服务器的注册与发现,有需求的时候调用zookeeper来发现可用的服务器,将任务均匀分配到各个服务器上去.这样