前言

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

django源码(2.0.2)粗解之命令行执行的更多相关文章

  1. [Go] gocron源码阅读-通过第三方cli包实现命令行参数获取和管理

    gocron源码中使用的是下面这个第三方包来实现的,下面就单独的拿出来测试以下效果,和官方flag包差不多 go get github.com/urfave/cli package main impo ...

  2. Kitex源码阅读——脚手架代码是如何通过命令行生成的(一)

    前言 Kitex是字节跳动内部的Golang微服务RPC框架,先已开源. Kitex文档:https://www.cloudwego.io/zh/docs/kitex/getting-started/ ...

  3. Django 源码小剖: URL 调度器(URL dispatcher)

    在刚开始接触 django 的时候, 我们尝试着从各种入门文档中创建一个自己的 django 项目, 需要在 mysite.urls.py 中配置 URL. 这是 django url 匹配处理机制的 ...

  4. Django 源码小剖: 初探中间件(middleware)

    因为考虑到文章的长度, 所以 BaseHandler 的展开被推迟了. 在 BaseHandler 中隐藏着中间件的信息, 较常见的 SessionMiddleware 就已经默认安装.  BaseH ...

  5. Django 源码小剖: Django ORM 查询管理器

    ORM 查询管理器 对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从 ...

  6. Django 源码小剖: 响应数据 response 的返回

    响应数据的返回 在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由 ...

  7. django源码阅读

    最近再看django-bootstrap-toolkit,一直困惑于静态文件的路径问题.所以只能从源码入手了.   从manage.py开始.manage.py 比较简单就几句话. #!/usr/bi ...

  8. Django 源码小剖: 初探 WSGI

    Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...

  9. 【集合框架】JDK1.8源码分析之ArrayList详解(一)

    [集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...

随机推荐

  1. MySQL字符集编码相关

    Windows 10家庭中文版,MySQL  5.7.20,2018-05-07 Part.1 查找数据库的字符集编码 查看MySQL字符集编码:status命令 使用命令行登录MySQL服务器,然后 ...

  2. PHP获得用户的真实IP地址

    <?php /** * 获得用户的真实IP地址 * * @access public * @return string */ function real_ip() { static $reali ...

  3. vim 中替换命令

    vi/vim 中可以使用 :s 命令来替换字符串.以前只会使用一种格式来全文替换,今天发现该命令有很多种写法(vi 真是强大啊,还有很多需要学习),记录几种在此,方便以后查询. :s/vivian/s ...

  4. 关于RundownProtect到底是什么东西

    RundownProtect这个字段相信只要是读过WRK源码的都会看过这个东西,这个字段在进程和线程的结构中都存在.最典型的例子就是对进程要进行什么操作的时候会先引用这个字段进行加保护,等操作结束后再 ...

  5. RCTF2015 pwn试题分析

    pwn200 漏洞给的很明显,先是读到了main的局部数组中,然后在子函数中向子函数的局部数组栈里复制. 总体思路是leak system的地址,然后再向一个固定地址写入/bin/sh,最后执行sys ...

  6. python之markdown转html

    python之markdown转html 为了方便,定义一个markdown转html的函数,直接调用即可 import markdown def md2html(mdstr): exts = ['m ...

  7. CSU训练分类

    √√第一部分 基础算法(#10023 除外) 第 1 章 贪心算法 √√#10000 「一本通 1.1 例 1」活动安排 √√#10001 「一本通 1.1 例 2」种树 √√#10002 「一本通 ...

  8. 磁盘清理-安全转移C盘中软件的缓存文件

    C盘飘红啦~~~ 安装软件时,默认会安装到C盘,并不会特意去改(尤其C盘是固态硬盘时).或者,根本就没有给你修改的机会. 可是啊,有些软件的缓存数据目录会比较大,实在太占C盘空间.想移出去,但又不想重 ...

  9. spring boot thymeleaf常用方式

    动态和静态区别 静态页面的return默认是跳转到/static/index.html,当在pom.xml中引入了thymeleaf组件,动态跳转会覆盖默认的静态跳转,默认就会跳转到/template ...

  10. (五)静态断言(下),static_assert

    二.静态断言与static_assert 通过上一篇,我们可以看到,断言assert宏只有在程序运行的时候才能起作用.而#error值在编译器预处理时才能起作用. 有时候,我们希望在编译时候能做一些断 ...