1. 程式人生 > 其它 >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