自定义django-admin命令

阅读: 22637     评论:1

我们可以通过manage.py编写和注册自定义的命令。

自定义的管理命令对于独立脚本非常有用,特别是那些使用Linux的crontab服务,或者Windows的调度任务执行的脚本。比如,假设你有个需求,需要定时清空某篇文章下面的评论,一种解决方案就是写一个django-admin命令,再写一个运行该命令的独立脚本,最后通过crontab服务,定时执行该脚本。

下面,我们将为教程最开始的第一个Django应用中的polls应用编写一个自定义的closepoll命令。

一、创建management/commands包

首先,需要在应用内添加一个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.stdoutself.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如何搜搜索命令了。

  1. 首先Django会从INSTALLED_APPS里按列表的顺序搜索命令
  2. 如果找到了,直接执行,后面的不再搜索。类似路由的短路机制。
  3. 如果没找到,去内置命令中查找,找到了就执行,没找到就弹出异常。

所以,为了覆盖一个命令,新命令必须有同样的名字并且它的app在 INSTALLED_APPS 中必须排在被覆盖的命令的 app 的前面。


 django-admin和manage.py 会话session 

评论总数: 1


点击登录后方可评论

在 二、编写命令的具体代码中 这一句话是为什么:" 注意:当你使用管理命令并希望提供控制台输出时,你应该写到self.stdout和self.stderr,而不能直接打印到stdout和stderr。 ",为什么不能直接使用sys.stdout.write,而是要使用self???