前言

 

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) + '\n')
 4             elif len(options.args) < 1: # print out the usages
 5                 sys.stdout.write(self.main_help_text() + '\n')
 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() + '\n')
12         elif self.argv[1:] in (['--help'], ['-h']):
13             sys.stdout.write(self.main_help_text() + '\n')
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.\n")
20             sys.stderr.write(
21                 "Unknown command: %r\nType '%s help' for usage.\n"
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

 

posted on 2018-03-21 18:02  o小小程序猿o  阅读(338)  评论(0编辑  收藏  举报