首页 > 编程语言 >03-Django命令 python manage.py runserver

03-Django命令 python manage.py runserver

时间:2023-01-17 11:11:56浏览次数:55  
标签:03 WSGI python py commands Django command django runserver

使用Django 开发,一定离不开这条命令,python manage.py runserver [127.0.0.1:8000],这条命令是启动DJango, 接下来就可以通过浏览器发起请求了。Django肯定也会遵守 WSGI协议,不出意外,我们开发的Django与WSGI服务器是完全解耦的,通过前面两篇博客,我们知道,WSGI 协议必定有一处地方启动 WSGI服务器,然后可能会接收一个 Handler,并且WSGI服务器接收到请求的时候,生成一个Handler对象,然后Handler对象里面执行了一个方法【也可以有其它的形式,WSGI协议的内容,后面会讲】,传入 environ 和 app,也就是WSGI协议的内容,服务器与APP的交互。

接下来我们通过断点调试寻找Django中的WSGI。

设置Django调试环境

从 manage.py 文件开始

可以看到 manage.py 中的main函数做了两件事,一件是导入了Django的配置文件,另外一件是将命令行的参数传入 execute_from_command_line 函数中

execute_from_command_line

execute_from_command_line 函数仅仅是生成了一个 ManagementUtility 对象,并且传入了命令行参数,然后执行了 ManagementUtility.execute()就结束了

所以我们需要进入 ManagementUtility 中一看究竟

ManagementUtility

ManagementUtility的初始化函数仅仅是完成赋值操作,没有任何奇特的地方。所以有必要看看 execute 方法。

ManagementUtility.execute

重点是我打断点的地方,前面的几乎都是依据选择做出配置项,看不出跟运行服务器相关的地方

由于 subcommand 的值是 runserver, 所以一定会走我打上断点的位置。看这段代码的字面意思,self.fetch_command(subcommand).run_from_argv(self.argv) 抓取一个 command,然后执行它的 run_from_argv 方法,抓取的 command 肯定是跟 runserver 相关啊,run_from_argv从参数开始运行。

这里我需要说明一下 Django 的目录结构,Django的每一个应用目录下都会有这么一个文件夹 management/commands 里面存放的是 Django 的脚本文件,我们按照固定的格式写脚本,然后使用 python manage.py xxx 来执行这个 xxx 脚本,而且并不需要完全的路径,Django 自己就会定位到具体的应用下的固定的脚本文件。而runserver也是一个脚本文件,她在哪里?她在 django包下的 core/management/commands 下。这些都是后话,后面的代码将会展示manage.py 是如何依据命令找到对应的脚本文件。这个方法就是 fetch_command,看名字也是他。

ManagementUtility.fetch_command

fetch_command 方法通过调用 get_commands 抓取所有的 commands,get_commands 非常暴力,它把整个Django系统中所有的 脚本文件都抓取出来了,而且我们也可以发现 runserver 的身影。不过我们先看看 get_commands

get_commands

@functools.lru_cache(maxsize=None)是一个缓存机制,可以缓存函数的结果,暂时不予理会,有兴趣的可以去百度一下。

此处,先看【1】,path

每一个python包都有一个 __path__变量,这是个内置变量,只在当前包中的__init__.py文件中存在并且生效,各位可以去试一下,我们这里的代码正好处于 __init__.py 文件内,可以调用

于是 command_dir = os.path.join(management_dir, 'commands') 这一行代码等于是获取了 django.core.management.commands这个目录,接着 pkgutil.iter_modules获取了所有的包,于是 find_commands 返回了所有的核心包下的所有脚本名

再看下面这段

这张图需要好好看看,我在实际调试过程中不是这个样子的,右侧调试器是没有那么多东西的,看代码我看不出 app_configs 是什么时候加载的,到目前为止,既然看不出来,那一定是我之前调试的时候跳过了某一步,也就是说 app_configs 早就被加载了,只是不知道什么时候,于是我这样打了断点

接着重新调试,没走几步就出现了前面那张复杂的图,在调试器中从下往上一层一层的看,原来是这里调用的。

上面这张图很复杂,不方便详细展开,读者可以自己调试看看,app_configs 早就加载了

于是这里通过同样的手段,得到了 settings 文件中注册的 APP,中提取了他们的所有的 commands 下的脚本文件。所以不光是 python manage.py runserver,其它的任意的脚本都是通过这种手段找到的。

load_command_class

load_command_class 意为加载 Command 类

import_module 做了一个导入动作,通过字符串拼接的手段,直接导入了 runserver 包,最后在返回的时候返回了一个 Command 对象,这个对象就是runserver 脚本文件中的 Command 类,熟悉Django的朋友应该知道这是个啥玩意儿,就不赘述了。但是需要注意的是,这个runserver可不是 core 下的runserver,而是 django.contrib.staticfiles.management.commands.runserver 这是怎么回事

django.contrib.staticfiles.management.commands.runserver

看到没,这个runserver并没有 command 方法,并且他继承自 django.core 下的runserver, 说明执行了它本质上还是执行了 django.core 下的runserver。

run_from_argv

可以看到 run_from_argv 中调用了 execute, 而 execute 中调用了我们自己写脚本的时候的 handle 方法,这个方法是需要自定义的,也是功能代码的主体。其余的细节读者可以自己深挖一下。

django.core 的 runserver

前面的代码其实只是为了讲述为什么 python manage.py runserver 可以调用 runserver 脚本。在这里我们将会详细阐述 runserver 脚本是如何启动一个 WSGI 服务器,以及跟 Django 应用交互的入口。在这之前,既然前面的代码我已经全部梳理通了,就需要去掉所有的断点,然后从 runserver 中开始打断点,探索 Django 中的 WSGI 的源码。

runserver.Command

可以看到重新调试,确实进入了 django.core 的 runserver 中,handle 的方法前面都是设置参数,关键是最后一个 run 方法,而 run 方法内,不管怎样,最后都会执行 inner_run 方法

inner_run

inner_run 方法太长,所以分页展示为两块。
【1】校验前一文件
【2,3】打印了我们每次启动Django开发测试的时候的一些信息
【4】获取了一个handler控制器。WSGI服务器有以下几个部分:服务器[负责监听端口,转发请求], 控制器[负责解析HTTP请求头,并且使用服务器返回的socket与客户端进行交互], app[需要我们自定义的部分,真正的处理前端请求的代码]
【5】看起来就是启动了一个WSGI服务器

get_handler

【1】这个 get_handler 是 django.contrib.staticfiles 下的 runserver 里的,但是 django.contrib.staticfiles 下的 runserver 继承了 django.core 下的 runserve
【2】又回过头调用了父类 django.core 下的 runserve 中的 get_handler
【3】调用了 get_internal_wsgi_application
【4】尝试获取配置文件中配置的 WSGI_APPLICATION,在我的项目中它是有值的,经过一系列寻找,他最后定位到了 django.core.handlers.wsgi.WSGIHandler 读者可以自己去查找一下,中间还要跳转几个文件,还涉及到我公司的项目,不方便展示,但是是可以找到的。

handler 已经找到了,接下来应该去看一下 run 方法了

run

先看左侧
【1】生成一个服务器对象
【2】设置app
【3】启动服务器
Django 也得遵守 WSGI 协议,在看右侧
【4】对应了服务器对象类
【5】处理请求的 Handler 类
【6】对应app,也就是那个 demo_app,但是这个名字取得很让人误会

到目前为止我们已经知道了 python manage.py runserver 确实开启了一个服务器,但是WSGI如何跟Django交互的我们还是没搞清楚,这将在下一篇博客继续探寻

标签:03,WSGI,python,py,commands,Django,command,django,runserver
From: https://www.cnblogs.com/yaowy001/p/17056125.html

相关文章

  • 15. Pytest常用插件:pytest-xdist分布式执行用例
    一、前言当我们自动化测试用例非常多的时候,一条条按顺序执行会非常慢,pytest-xdist的出现就是为了让自动化测试用例可以分布式执行,从而节省自动化测试时间,pytest-xdist是......
  • 14.Pytest常用插件:pytest-assume多重断言
    一、前言在自动化测试过程中,我们执行完用例之后,需要验证脚本执行的结果和预期的结果是否一致,来达到断言测试用例是否执行成功。一般情况下我们常用的断言方式是assert+......
  • 16. Pytest常用插件:pytest-html生成简洁的测试报告
    一、前言我们都知道,在功能测试过程中,执行完测试用例后,最终要交付测试报告来直观的展示测试结果。自动化测试也是同样,脚本运行完之后,需要有一份测试报告对测试执行的结果进......
  • 18. Pytest常用插件:allure-pytest生成精美的测试报告(二)
    一、前言前面我们简单介绍了如何快速生成一个allure测试报告,其实allure也是可以自定义展示的。allure报告可以自定义展示很多详细的信息描述测试用例,包括epic、feature、s......
  • 20. Pytest.ini文件介绍
    一、前言pytest.ini文件是Pytest的主配置文件,可以改变Pytest的运行方式,pytest.ini文件的名字是固定的,不能改动。pytest.ini文件可以改变Pytest测试框架默认的行为(查找执......
  • 19. Pytest执行用例的多种方式
    一、前言我们在前面的内容中其实已经讲解了pytest用例的执行,本节课对主要针对执行用例的方式进行一个汇总。二、学习目标1.通过IDE运行2.通过命令行运行3.通过main函......
  • 5. Pytest自定义前置后置:fixture参数详解(了解)
    一、前言我们上节课讲到fixture自定义前置函数的时候,有5个非必填参数,scope,params,autouse,ids,name。一般情况下这五个参数我们在工作中都不常用,但是个别情况会用到,这节......
  • 4. Pytest自定义前置后置:fixture简单应用
    一、前言前面讲到用例加setup和teardown可以实现在测试用例之前或之后加入一些操作,但这种是整个脚本全局生效的,如果我想实现指定某一个执行前置,另一个不执行前置,则需......
  • 6.Pytest参数化:parametrize简单应用
    一、前言参数化的目的是为了使测试用例代码与测试数据进行分离,这样,测试人员只需要维护测试数据,而不用更改代码。pytest模块给我们提供了专门用来参数化的装饰器:@pytest.ma......
  • 7. Pytest参数化:parametrize参数详解(了解)
    一、前言上节课我们讲解了如何用parametrize装饰器进行参数化,它有5个参数,argnames,argvalues,indirect,ids,scope,本节课针对这五个参数做详细讲解。二、学习目标1.a......