前言

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

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

命令行执行入口

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

 from django.core import management

 if __name__ == "__main__":
management.execute_from_command_line()

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

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

 def execute_from_command_line(argv=None):
"""Run a ManagementUtility."""
utility = ManagementUtility(argv)
utility.execute()

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

ManagementUtility类

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

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

     def __init__(self, argv=None):
self.argv = argv or sys.argv[:] # 从传入的参数获得,如果没有传入参数就从sys.argv中去取
self.prog_name = os.path.basename(self.argv[0])
if self.prog_name == '__main__.py':
self.prog_name = 'python -m django'
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,以便确定后续执行什么任务。

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

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

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

 class CommandParser(ArgumentParser):
"""
Customized ArgumentParser class to improve some error messages and prevent
SystemExit in several occasions, as SystemExit is unacceptable when a
command is called programmatically.
"""
def __init__(self, cmd, **kwargs):
self.cmd = cmd
super().__init__(**kwargs) def parse_args(self, args=None, namespace=None):
# Catch missing argument for a better error message
if (hasattr(self.cmd, 'missing_args_message') and
not (args or any(not arg.startswith('-') for arg in args))):
self.error(self.cmd.missing_args_message)
return super().parse_args(args, namespace) def error(self, message):
if self.cmd._called_from_command_line:
super().error(message)
else:
raise CommandError("Error: %s" % message)

argparse官方文档 | argparse用法总结

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

         if subcommand == 'help':
if '--commands' in args: # only print the commands only
sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
elif len(options.args) < 1: # print out the usages
sys.stdout.write(self.main_help_text() + '\n')
else:
self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
# Special-cases: We want 'django-admin --version' and
# 'django-admin --help' to work, for backwards compatibility.
elif subcommand == 'version' or self.argv[1:] == ['--version']:
sys.stdout.write(django.get_version() + '\n')
elif self.argv[1:] in (['--help'], ['-h']):
sys.stdout.write(self.main_help_text() + '\n')
else:
self.fetch_command(subcommand).run_from_argv(self.argv)

最后看一眼fetch_command的代码:

     def fetch_command(self, subcommand):
"""
Try to fetch the given subcommand, printing a message with the
appropriate command called from the command line (usually
"django-admin" or "manage.py") if it can't be found.
"""
# Get commands outside of try block to prevent swallowing exceptions
commands = get_commands()
try:
app_name = commands[subcommand]
except KeyError:
if os.environ.get('DJANGO_SETTINGS_MODULE'):
# If `subcommand` is missing due to misconfigured settings, the
# following line will retrigger an ImproperlyConfigured exception
# (get_commands() swallows the original one) so the user is
# informed about it.
settings.INSTALLED_APPS
else:
sys.stderr.write("No Django settings specified.\n")
sys.stderr.write(
"Unknown command: %r\nType '%s help' for usage.\n"
% (subcommand, self.prog_name)
)
sys.exit(1)
if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly.
klass = app_name
else:
klass = load_command_class(app_name, subcommand)
return klass

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

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

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

 class Command(BaseCommand):
help = "Checks the entire Django project for potential problems." requires_system_checks = False #...

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

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

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. cocos2dx中调用TinyXml读取xml配置文件 || cocos2d-x 中跨平台tinyxml读取xml文件方式

    TiXmlDocument *doc = newTiXmlDocument; #if (CC_TARGET_PLATFORM ==CC_PLATFORM_ANDROID) //Android平台tin ...

  2. 06 Frequently Asked Questions (FAQ) 常见问题解答 (常见问题)

    Frequently Asked Questions (FAQ) Origins 起源 What is the purpose of the project? What is the history ...

  3. 洛谷P1782 旅行商的背包

    传送门啦 这个题不用二进制优化的话根本不行,现学的二进制优化,调了一段时间终于A了,不容易.. 如果不懂二进制优化的话可以去看我那个博客 二进制优化多重背包入口 不想TLE,不要打memset,一定要 ...

  4. Java容器---Set: HashSet & TreeSet & LinkedHashSet

    1.Set接口概述        Set 不保存重复的元素(如何判断元素相同呢?).如果你试图将相同对象的多个实例添加到Set中,那么它就会阻止这种重复现象. Set中最常被使用的是测试归属性,你可以 ...

  5. linux内核之accept实现

    用户态对accept的标准用法: if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size) ...

  6. Python练手之爬虫

    很久没更新博客了,最近自学Python,写个在百度上爬算法题题解的爬虫,第一次写爬虫..纯当练手 慢慢来.. #coding:utf-8 ''' Created on 2016年11月22日 @aut ...

  7. 【Sequel Pro】下载查询结果乱码问题处理方式

    1.下载查询结果已CSV格式保存 2.出现乱码问题样式如下截图: 3. 右键下载的CSV文件,选择用文本编辑打开 4.全选打开的页面内容,重新新建“文本编辑”并进行“储存” 5.打开 1.CSV,则看 ...

  8. PostgreSQL数据库如果不存在则插入,存在则更新

    INSERT INTO UM_CUSTOMER(customercode,CompanyFlag,InputTime,LocalVersion) ) ON conflict(customercode) ...

  9. Android Studio编译慢、卡死和狂占内存怎么破?

    https://www.zhihu.com/question/27953288 作者:知乎用户链接:https://www.zhihu.com/question/27953288/answer/118 ...

  10. JS黑魔法之this, setTimeout/setInterval, arguments

    最近发现了JavaScript Garden这个JS黑魔法收集处,不过里面有一些东西并没有说得很透彻,于是边看边查文档or做实验,写了一些笔记,顺手放在博客.等看完了You don't know JS ...