专栏首页扎心了老铁Django扩展自定义manage命令

Django扩展自定义manage命令

使用django开发,对python manage.py ***命令模式肯定不会陌生。比较常用的有runserver,migrate。。。

本文讲述如何自定义扩展manage命令。

1、源码分析

manage.py文件是通过django-admin startproject project_name生成的。

1)manage.py的源码

a)首先设置了settings文件,本例中CIServer指的是project_name。

b)其次执行了一个函数django.core.management.execute_from_command_line(sys.argv),这个函数传入了命令行参数sys.argv

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CIServer.settings")
    try:
        from django.core.management import execute_from_command_line
    except ImportError:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and available "
            "on your PATH environment variable? Did you forget to activate a "
            "virtual environment?"
        )
    execute_from_command_line(sys.argv)

2)execute_from_command_line

里面调用了ManagementUtility类中的execute方法

def execute_from_command_line(argv=None):
    """
    A simple method that runs a ManagementUtility.
    """
    utility = ManagementUtility(argv)
    utility.execute()

在execute中主要是解析了传入的参数sys.argv,并且调用了get_command()

3)get_command

def get_commands():
    """
    Returns a dictionary mapping command names to their callback applications.

    This works by looking for a management.commands package in django.core, and
    in each installed application -- if a commands package exists, all commands
    in that package are registered.

    Core commands are always included. If a settings module has been
    specified, user-defined commands will also be included.

    The dictionary is in the format {command_name: app_name}. Key-value
    pairs from this dictionary can then be used in calls to
    load_command_class(app_name, command_name)

    If a specific version of a command must be loaded (e.g., with the
    startapp command), the instantiated module can be placed in the
    dictionary in place of the application name.

    The dictionary is cached on the first call and reused on subsequent
    calls.
    """
    commands = {name: 'django.core' for name in find_commands(upath(__path__[0]))}

    if not settings.configured:
        return commands

    for app_config in reversed(list(apps.get_app_configs())):
        path = os.path.join(app_config.path, 'management')
        commands.update({name: app_config.name for name in find_commands(path)})

    return commands

get_command里遍历所有注册的INSTALLED_APPS路径下的management寻找(find_commands)用户自定义的命令。

def find_commands(management_dir):
    """
    Given a path to a management directory, returns a list of all the command
    names that are available.

    Returns an empty list if no commands are defined.
    """
    command_dir = os.path.join(management_dir, 'commands')
    # Workaround for a Python 3.2 bug with pkgutil.iter_modules
    sys.path_importer_cache.pop(command_dir, None)
    return [name for _, name, is_pkg in pkgutil.iter_modules([npath(command_dir)])
            if not is_pkg and not name.startswith('_')]

可以发现并注册的命令是commands目录下不以"_"开头的文件名。

4)load_command_class

将命令文件***.py中的Command类加载进去。

def load_command_class(app_name, name):
    """
    Given a command name and an application name, returns the Command
    class instance. All errors raised by the import process
    (ImportError, AttributeError) are allowed to propagate.
    """
    module = import_module('%s.management.commands.%s' % (app_name, name))
    return module.Command()

5)Command类

Command类要继承BaseCommand类,其中很多方法,一定要实现的是handle方法,handle方法是命令实际执行的代码。

2、如何实现自定义命令

根据上面说的原理,我们只需要在app目录中建立一个目录management,并且在下面建立一个目录叫commands,下面增加一个文件,叫hello.py即可。

要注意一下几点

1)说是目录,其实应该是包,所以在management下面和commands下面都要添加__init__.py。

2)app一定要添加在INSTALLED_APPS中,不然命令加载不到。

应该是这样的

在hello.py中实现命令的具体内容

#-*- coding:utf-8 -*-
from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):

    def add_arguments(self, parser):

        parser.add_argument(
            '-n',
            '--name',
            action='store',
            dest='name',
            default='close',
            help='name of author.',
        )

    def handle(self, *args, **options):
        try:
            if options['name']:
                print 'hello world, %s' % options['name']
            
            self.stdout.write(self.style.SUCCESS('命令%s执行成功, 参数为%s' % (__file__, options['name'])))
        except Exception, ex:
            self.stdout.write(self.style.ERROR('命令执行出错'))

 执行方式

python manage.py hello -n kangaroo

hello world, kangaroo
命令hello.py执行成功,参数为kangaroo

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • springboot接口访问权限AOP实现

    现在有个系统,很多接口只需要登录就可以访问,但是有些接口需要授予并验证权限。如果用注解controller的方式控制接口的权限呢?

    用户1225216
  • RabbitMQ高可用集群配置

    1、安装RabbitMQ 1)下载和安装erlang 下载erlang wget http://www.rabbitmq.com/releases/erlang...

    用户1225216
  • django使用xlwt导出excel文件

    这里只是mark一下导出的方法,并没有做什么REST处理和异常处理。 维护统一的style样式,可以使导出的数据更加美观。 def export_excel(r...

    用户1225216
  • CRM客户关系管理系统(十一) 第十一章、学员报名流程开发

    zhang_derek
  • python 实战 登录注册

    py3study
  • FeignClient源码深度解析

    大体步骤: 一、注册FeignClient配置类和FeignClient BeanDefinition 二、实例化Feign上下文对象FeignContext ...

    方志朋
  • Hadoop框架:单服务下伪分布式集群搭建

    以下配置文件所在路径:/opt/hadoop2.7/etc/hadoop,这里是Linux环境,脚本配置sh格式。

    知了一笑
  • python 面向对象基础 实例属性和类属性

    但是,如果Student类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,归Student类所有:

    葫芦
  • Python | super函数是如何来继承的

    Py 2.x 和 Py 3.x 中有一个很大的区别就是类,无论是类的定义还是类的继承。Py 3.x 中类的继承可以直接使用 super() 关键字代替原来的 s...

    用户1634449
  • Python 的 __name__ 变量,到底是个什么东西?

    大家应该已经在很多 Python 脚本里见到过 name 变量了吧?它经常是以类似这样的方式出现在我们的程序里:

    昱良

扫码关注云+社区

领取腾讯云代金券