django源碼(2.0.2)粗解之命令行執行

前言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這個類中實現的。函數

 

ManagementUtilityfetch

 

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)

 

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

相關文章
相關標籤/搜索