• django源码(2.0.2)粗解之命令行执行


    前言

    django的命令行在整个的django web开发中都会经常用到,而且是必须得用到。所以,能够了解下django的命令行实现其实是非常有帮助的。

    如果大家比较关心django命令的详细说明和使用,可以查看这里

    命令行执行入口

    django通过django-admin.py和manage.py来执行命令,以下是这两个文件的源码:

    1 from django.core import management
    2 
    3 if __name__ == "__main__":
    4     management.execute_from_command_line()

    它们都调用了management模块下的execute_from_command_line()方法。

    这个方法是在django/core/management/__init__.py中定义:

    1 def execute_from_command_line(argv=None):
    2     """Run a ManagementUtility."""
    3     utility = ManagementUtility(argv)
    4     utility.execute()

    实现非常简单:生成一个ManagementUtility对象,并让这个对象执行相应的命令行命令。所以主要的工作都是在ManagementUtility这个类中实现的。

    ManagementUtility

    python是一门面向的对象的语言,django作为python的一个著名web框架,它所使用当然也是面向对象的思想。所以我们在分析源码的时候应该尽量用面向对象的思想去思考。

    ManagementUtility具有3个属性,我们可以从它的__init__函数中看到

    1     def __init__(self, argv=None):
    2         self.argv = argv or sys.argv[:] # 从传入的参数获得,如果没有传入参数就从sys.argv中去取
    3         self.prog_name = os.path.basename(self.argv[0])
    4         if self.prog_name == '__main__.py':
    5             self.prog_name = 'python -m django'
    6         self.settings_exception = None

    self.argv:命令行信息,包括命令和参数

    self.prog_name:程序名

    self.settings_excepiton:settings的异常信息,发现settings的设置有异常,会将异常信息存在这个变量里面

    ManagementUtility主要的方法是execute(),它完成了command执行的所有过程。

    1. 我们知道,django的命令行是具有一定的格式的,都是 command subcommand [arguments],arguments有时是可选的。所以execute方法第一步就是获得subcommand,以便确定后续执行什么任务。

    1         try:
    2             subcommand = self.argv[1]
    3         except IndexError:
    4             subcommand = 'help'  # Display help if no arguments were given.    

    这里提一下,为什么不先获取command呢?其实command是系统用来找程序入口的。

    2. 用命令解析器CommandParser解析命令行。CommandParser继承了argparse模块的ArgumentParser类,但它只是对ArgumentParser的异常处理进行了加强。

     1 class CommandParser(ArgumentParser):
     2     """
     3     Customized ArgumentParser class to improve some error messages and prevent
     4     SystemExit in several occasions, as SystemExit is unacceptable when a
     5     command is called programmatically.
     6     """
     7     def __init__(self, cmd, **kwargs):
     8         self.cmd = cmd
     9         super().__init__(**kwargs)
    10 
    11     def parse_args(self, args=None, namespace=None):
    12         # Catch missing argument for a better error message
    13         if (hasattr(self.cmd, 'missing_args_message') and
    14                 not (args or any(not arg.startswith('-') for arg in args))):
    15             self.error(self.cmd.missing_args_message)
    16         return super().parse_args(args, namespace)
    17 
    18     def error(self, message):
    19         if self.cmd._called_from_command_line:
    20             super().error(message)
    21         else:
    22             raise CommandError("Error: %s" % message)

    argparse官方文档 | argparse用法总结

    3. 解析器解析出了subcommand的arguments,然后fetch_command根据subcommand导入相应的command包并生成相应的command对象,然后调用command对象的print_help方法或者run_from_argv方法去执行相应的命令。

     1         if subcommand == 'help':
     2             if '--commands' in args:    # only print the commands only
     3                 sys.stdout.write(self.main_help_text(commands_only=True) + '
    ')
     4             elif len(options.args) < 1: # print out the usages
     5                 sys.stdout.write(self.main_help_text() + '
    ')
     6             else: 
     7                 self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
     8         # Special-cases: We want 'django-admin --version' and
     9         # 'django-admin --help' to work, for backwards compatibility.
    10         elif subcommand == 'version' or self.argv[1:] == ['--version']:
    11             sys.stdout.write(django.get_version() + '
    ')
    12         elif self.argv[1:] in (['--help'], ['-h']):
    13             sys.stdout.write(self.main_help_text() + '
    ')
    14         else:
    15             self.fetch_command(subcommand).run_from_argv(self.argv)        

    最后看一眼fetch_command的代码:

     1     def fetch_command(self, subcommand):
     2         """
     3         Try to fetch the given subcommand, printing a message with the
     4         appropriate command called from the command line (usually
     5         "django-admin" or "manage.py") if it can't be found.
     6         """
     7         # Get commands outside of try block to prevent swallowing exceptions
     8         commands = get_commands()
     9         try:
    10             app_name = commands[subcommand]
    11         except KeyError:
    12             if os.environ.get('DJANGO_SETTINGS_MODULE'):
    13                 # If `subcommand` is missing due to misconfigured settings, the
    14                 # following line will retrigger an ImproperlyConfigured exception
    15                 # (get_commands() swallows the original one) so the user is
    16                 # informed about it.
    17                 settings.INSTALLED_APPS
    18             else:
    19                 sys.stderr.write("No Django settings specified.
    ")
    20             sys.stderr.write(
    21                 "Unknown command: %r
    Type '%s help' for usage.
    "
    22                 % (subcommand, self.prog_name)
    23             )
    24             sys.exit(1)
    25         if isinstance(app_name, BaseCommand):
    26             # If the command is already loaded, use it directly.
    27             klass = app_name
    28         else:
    29             klass = load_command_class(app_name, subcommand)
    30         return klass

    这里主要用了load_command_class去导入相应的subcommand模块,并生成了一个command类对象。

    1 def load_command_class(app_name, name):
    2     """
    3     Given a command name and an application name, return the Command
    4     class instance. Allow all errors raised by the import process
    5     (ImportError, AttributeError) to propagate.
    6     """
    7     module = import_module('%s.management.commands.%s' % (app_name, name))
    8     #print("import %s %s" %(app_name, name))
    9     return module.Command()

    我们可以去django/core/management/command/目录下随便找一个command模块看一眼,比如说check.py,每个模块都有一个command类并继承自BaseCommand。上面提到的print_help方法或者run_from_argv方法都是在BaseCommand类中实现。

    1 class Command(BaseCommand):
    2     help = "Checks the entire Django project for potential problems."
    3 
    4     requires_system_checks = False
    5     
    6     #...

    -------------------------------------------------

    以上是我的一点粗浅的理解,如果有觉得不对的地方,请多多指教,非常感谢!

    2018-03-21 17:57:17

  • 相关阅读:
    洛谷 U141580 简化罗波切问题
    洛谷 U141578 维修电路
    洛谷 U140760 狭义公因子
    CF75C Modified GCD
    算法题-求解斐波那切数列的第N个数是几?
    算法题-求N的阶乘
    JAVA8新特性
    nginx启动脚本,手动编辑
    javah生成带有包名的头文件
    Matlab图像处理(03)-基本概念
  • 原文地址:https://www.cnblogs.com/XXCXY/p/8618662.html
Copyright © 2020-2023  润新知