我们可以通过manage.py编写和注册自定义的命令。
自定义的管理命令对于独立脚本非常有用,特别是那些使用Linux的crontab服务,或者Windows的调度任务执行的脚本。比如,假设你有个需求,需要定时清空某篇文章下面的评论,一种解决方案就是写一个django-admin命令,再写一个运行该命令的独立脚本,最后通过crontab服务,定时执行该脚本。
下面,我们将为教程最开始的第一个Django应用中的polls应用编写一个自定义的closepoll
命令。
首先,需要在应用内添加一个management/commands
包。
Django将为该目录中,名字没有以下划线开始的,每个Python模块注册一个manage.py命令。也就是说,你每自定义一个命令,就要在目录下创建一个新的模块。
像这样:
polls/ __init__.py models.py management/ __init__.py commands/ __init__.py _private.py closepoll.py tests.py views.py
在这个例子中,closepoll
命令对任何项目都可用,只要它们的INSTALLED_APPS
里包含polls应用。
_private.py
因为以下划线开头,将不可以作为一个管理命令使用。
在closepoll.py
模块中只有一个要求:必须定义一个Command
类并继承BaseCommand
类或其子类。
要实现这个命令,在polls/management/commands/closepoll.py
中添加代码如下:
from django.core.management.base import BaseCommand, CommandError from polls.models import Question as Poll class Command(BaseCommand): help = "关闭指定调查问卷" def add_arguments(self, parser): parser.add_argument("poll_ids", nargs="+", type=int) def handle(self, *args, **options): for poll_id in options["poll_ids"]: try: poll = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise CommandError('Poll "%s" does not exist' % poll_id) poll.opened = False poll.save() self.stdout.write( self.style.SUCCESS('Successfully closed poll "%s"' % poll_id) )
每一个自定义的命令,都要自己实现handle()
方法,这个方法是命令的核心业务处理代码,你的命令功能要通过它来实现。而add_arguments()
则用于帮助你处理命令行的参数,如果没有参数,可以不写这个方法。
注意:当你使用管理命令并希望提供控制台输出时,你应该写入self.stdout
和self.stderr
,而不能直接打印到stdout和stderr。另外,你不需要自己在消息的末尾加上换行符,它将被自动添加,除非你指定ending参数:self.stdout.write("Unterminated line", ending='')
Django内部使用argsparse库来解析命令。
nargs
参数: 应该读取的参数个数,可以是具体的数字,或者是?号。或者是 * 号,表示 0 或多个参数;或者是 + 号表示 1 或多个参数。调用格式:python manage.py 自定义命令名 <参数列表>
可以使用python manage.py closepoll <poll_id>
调用上面的自定义命令。
可以在add_arguments()
方法中为命令添加参数:
class Command(BaseCommand): def add_arguments(self, parser): # 添加位置参数 parser.add_argument('poll_ids', nargs='+', type=int) # 添加可选的关键字参数 parser.add_argument( '--delete', action='store_true', help='Delete poll instead of closing it', ) def handle(self, *args, **options): # ... if options['delete']: poll.delete() # ...
action='store_true'
表示可以不指定该参数的值。
绝大多数情况下,我们都是创造新功能,所以命令的名字,不能和Django内置的一样,甚至不能和第三方库的命令一样。
但有时候,你可能在编写覆盖已有命令的功能,这就需要了解Django如何搜搜索命令了。
INSTALLED_APPS
里按列表的顺序搜索命令所以,为了覆盖一个命令,新命令必须有同样的名字并且它的app在 INSTALLED_APPS
中必须排在被覆盖的命令的 app 的前面。
在 二、编写命令的具体代码中 这一句话是为什么:" 注意:当你使用管理命令并希望提供控制台输出时,你应该写到self.stdout和self.stderr,而不能直接打印到stdout和stderr。 ",为什么不能直接使用sys.stdout.write,而是要使用self???