前言html
django的命令行在整個的django web開發中都會常常用到,並且是必須得用到。因此,可以瞭解下django的命令行實現實際上是很是有幫助的。python
若是你們比較關心django命令的詳細說明和使用,能夠查看這裏。web
命令行執行入口django
django經過django-admin.py和manage.py來執行命令,如下是這兩個文件的源碼:app
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中定義:ide
1 def execute_from_command_line(argv=None): 2 """Run a ManagementUtility.""" 3 utility = ManagementUtility(argv) 4 utility.execute()
實現很是簡單:生成一個ManagementUtility對象,並讓這個對象執行相應的命令行命令。因此主要的工做都是在ManagementUtility這個類中實現的。函數
ManagementUtility類fetch
python是一門面向的對象的語言,django做爲python的一個著名web框架,它所使用固然也是面向對象的思想。因此咱們在分析源碼的時候應該儘可能用面向對象的思想去思考。ui
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)
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