Django源码解析(二) manage.py

Django源码解析(一) 开篇

Django源码解析(二) manage.py

Django源码解析(三) Django开发服务器,WSGI规范实现

Django源码解析(四) 中间件

Django源码解析(五) URL配置

 

一. 什么是manage.py

在使用django-admin.py创建Django项目时,manage.py会被自动生成在项目根目录下.用以对django项目实现命令行操作.

调用django.core.management .execute_manager()方法实现命令执行.

#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
    imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
    import sys
    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
    sys.exit(1)

import settings

if __name__ == "__main__":
    execute_manager(settings)

二. manage.py与django-admin.py的区别

django-admin.py调用django.core.management .execute_from_command_line()方法实现命令执行.

#!/usr/bin/env python
from django.core import management

if __name__ == "__main__":
    management.execute_from_command_line()

manage.py与django-admin.py都使用了django.core.management包的方法.execute_manager()方法专门提供给manage.py使用,execute_from_command_line()专门

提供给django-admin.py使用.(地球人都看的出来,⊙﹏⊙b).

这俩个命令的主要差别在于execute_manager()多执行了一个setup_environ(settings_mod)方法.弄清这个方法的作用,就知道manage.py与django-admin.py的主要区别了.

def execute_manager(settings_mod, argv=None):
    """
    Like execute_from_command_line(), but for use by manage.py, a
    project-specific django-admin.py utility.
    """
    setup_environ(settings_mod)
    utility = ManagementUtility(argv)
    utility.execute()

最主要显示setup_environ(settings_mod)方法功能的代码:

    # Set DJANGO_SETTINGS_MODULE appropriately.
    if original_settings_path:
        os.environ['DJANGO_SETTINGS_MODULE'] = original_settings_path
    else:
        os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)

    # Import the project module. We add the parent directory to PYTHONPATH to
    # avoid some of the path errors new users can have.
    sys.path.append(os.path.join(project_directory, os.pardir))
    project_module = import_module(project_name)
    sys.path.pop()

可见setup_environ(settings_mod)方法的主要功能,即manage.py与django-admin.py的主要区别:

manage.py设置一个名为DJANGO_SETTINGS_MODULE的系统环境变量.

例如我的django项目根目录为”E:\workspace\django\pearl”,settings模块的文件名为settings.py.DJANGO_SETTINGS_MODULE会被设置成为”pearl.settings”

  • manage.py把当前项目目录的父目录加入到PYTHONPATH,项目目录作为包导入.

例如我的django项目根目录为”E:\workspace\django\pearl”.把”E:\workspace\django”加入到PYTHONPATH,导入包名为”pearl”.

以上2点实际上使在使用manage.py时,不用像使用django-admin.py时输入”--settings”与”--pythonpath”参数.提供更便捷的操作.

注: 当直接使用django-admin.py时,需要设置'DJANGO_SETTINGS_MODULE'环境变量或”—settings”选项.

三. manage.py命令执行过程

通过execute_manager()与execute_from_command_line()方法,可以看到Django命令的执行主要通过django.core.management.ManagementUtility类执行.

ManagementUtility类的execute()方法为执行入口.

命令的执行过程:

1.解析命令,获得要执行的子命令名称.通过继承至OptionParser的类LaxOptionParser解析命令行.

2.获得所有可执行子命令的完整模块路径.

命令文件在django/core/management/commands目录下与INSTALLED_APPS(settings.py文件中定义)/management/commands目录下.

通过ManagementUtility.fetch_command()方法中,调用get_commands()方法实现.

3.返回子命令对应同名模块中的Command类的实例.

4.执行命令.调用Command类的实例的run_from_argv()方法.

 

def handle_default_options(options):
    """
    此方法设定'DJANGO_SETTINGS_MODULE'与pythonpath,即在manage.py中设定的2个内容
    """
    if options.settings:
        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
    if options.pythonpath:
        sys.path.insert(0, options.pythonpath)


class ManagementUtility(object):
    """
    Encapsulates the logic of the django-admin.py and manage.py utilities.

    A ManagementUtility has a number of commands, which can be manipulated
    by editing the self.commands dictionary.
    """
    def __init__(self, argv=None):
    	# 获取输入参数
        self.argv = argv or sys.argv[:]
        # 执行命令的文件,manage.py或django-admin.py
        self.prog_name = os.path.basename(self.argv[0])

    ############################################################################
    # 省略
    ############################################################################

    def fetch_command(self, subcommand):
        """
        Tries to fetch the given subcommand, printing a message with the
        appropriate command called from the command line (usually
        "django-admin.py" or "manage.py") if it can't be found.
        """
        try:
        	# 获得django/core/management/commands目录下与INSTALLED_APPS/management/commands目录下的子命令对应的模块前缀
        	# 如"cleanup"对应模块前缀"django.core"
            app_name = get_commands()[subcommand]
        except KeyError:
            sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
                (subcommand, self.prog_name))
            sys.exit(1)
        if isinstance(app_name, BaseCommand):
            # If the command is already loaded, use it directly.
            klass = app_name
        else:
        	# load_command_class的核心代码,返回指定子命令模块Command类的实例.
        	# module = import_module('%s.management.commands.%s' % (app_name, name))
    		# return module.Command()
            klass = load_command_class(app_name, subcommand)
        return klass

    ############################################################################
    # 省略
    ############################################################################

    def execute(self):
        """
        Given the command-line arguments, this figures out which subcommand is
        being run, creates a parser appropriate to that command, and runs it.
        主要执行入口.
        通过继承至OptionParser的类LaxOptionParser解析命令行.执行相应的子命令.
        """
        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
                                 version=get_version(),
                                 option_list=BaseCommand.option_list)
        self.autocomplete()
        try:
            options, args = parser.parse_args(self.argv)
            # 如果输入了settings与pythonpath参数的话,设置这俩个参数
            handle_default_options(options)
        except:
            pass # Ignore any option errors at this point.

        try:
        	# 要执行的子命令 如: "manage.py runserver"中的"runserver"
            subcommand = self.argv[1]
        except IndexError:
        	# 未输入子命令的话,打印"help"信息
            subcommand = 'help' # Display help if no arguments were given.

        if subcommand == 'help':
            if len(args) > 2:
                self.fetch_command(args[2]).print_help(self.prog_name, args[2])
            else:
                parser.print_lax_help()
                sys.stderr.write(self.main_help_text() + '\n')
                sys.exit(1)
        # Special-cases: We want 'django-admin.py --version' and
        # 'django-admin.py --help' to work, for backwards compatibility.
        elif self.argv[1:] == ['--version']:
            # LaxOptionParser already takes care of printing the version.
            pass
        elif self.argv[1:] in (['--help'], ['-h']):
            parser.print_lax_help()
            sys.stderr.write(self.main_help_text() + '\n')
        else:
        	# 找到相应子命令,并执行.
            self.fetch_command(subcommand).run_from_argv(self.argv)

插播广告: settings.SETTINGS_MODULE

时常用到django.conf.settings的SETTINGS_MODULE属性来关联到我们自己项目的settings模块.它是如何实现的呢?

嘿嘿,这个是通过前面提到的DJANGO_SETTINGS_MODULE系统环境变量来实现的.来看这个实现的核心代码.

ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"

settings_module = os.environ[ENVIRONMENT_VARIABLE]

self.SETTINGS_MODULE = settings_module

四. 命令

Django命令需要写在与命令行子命令同名的模块中.模块必须包含一个名为Command的类作为调用入口.

django.core.management.BaseCommand类作为所有命令的基类.还同时提供了AppCommand LabelCommand和NoArgsCommand三个辅助类.

自定义命令可继承自BaseCommand或其他三个辅助类,同时重写父类的不同方法即可.

简单介绍

1. BaseCommand

所有命令的基础类,预留一个handle()方法,供子类重写.

def handle(self, *args, **options):
        """
        The actual logic of the command. Subclasses must implement
        this method.

        """
        raise NotImplementedError()

2. AppCommand

接受一个或多个installed application的名字作为第一部分参数.实现对App操作.预留一个handle_app()方法供子类重写.

def handle_app(self, app, **options):
        """
        Perform the command's actions for ``app``, which will be the
        Python module corresponding to an application name given on
        the command line.

        """
        raise NotImplementedError()

3. LabelCommand

接受一个或多个标签参数,预留一个handle_label()方法供子类重写.

def handle_label(self, label, **options):
        """
        Perform the command's actions for ``label``, which will be the
        string as given on the command line.

        """
        raise NotImplementedError()

4. NoArgsCommand

不接受参数.预留一个handle_label()方法供子类重写.

def handle_noargs(self, **options):
        """
        Perform this command's actions.

        """
        raise NotImplementedError()

转载于:https://www.cnblogs.com/pythoner/archive/2011/07/30/2121599.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值