django python manage.py runserver 流程
python manage.py runserver 流程分析
这是自己的学习笔记,水平有限,很多地方没有仔细看,慎重阅读
启动 django demo
去 GitHub 的 Django 项目中下载 django-1.0.tar.gz,这是早期的版本,易于理解核心逻辑,并包含一个示例。
解压 django-1.0.tar.gz,目录 django 就是源代码,examples 是一个简单地 web 示例。
启动示例需要依赖 Django,我们直接把 django 目录移动到示例目录下解决依赖问题。
像这样:
├─examples
│ ├─django
│ └─hello
进入 examples 目录,运行 demo
# Python2.7
python manage.py runserver
访问 http://127.0.0.1:8000/
可以看到一个非常简单的网页
源码分析
以下代码存在删减,主要展示代码流程
从 manage.py 开始,执行了 execute_manager 方法,传入 settings 模块
execute_manager(settings)
django.core.management.execute_manager 方法
def execute_manager(settings_mod, argv=None):
# setup_environ 函数,只是设置了环境变量,执行配置模块
# os.environ['DJANGO_SETTINGS_MODULE'] = examples.settings
setup_environ(settings_mod)
# admin manage 工具类
utility = ManagementUtility(argv)
utility.execute()
ManagementUtility.execute
class ManagementUtility(object):
def __init__(self, argv=None):
# 初始化,例如
self.argv = ['.../examples/manage.py', 'runserver']
self.prog_name = 'manage.py'
def execute(self):
# 删除了部分代码,最终执行代码大致如下
# 这是一个参数解析工具,帮助处理参数信息
parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
version=get_version(),
option_list=BaseCommand.option_list)
# 获取启动时指定的选项和参数
# 有些影响重大的选项必须提前处理
options, args = parser.parse_args(self.argv)
# handle_default_options 主要做了两件事
# --settings 选项,覆盖默认的配置文件
# --pythonpath 选项,指定 python 解释器路径
# 对于这个例子来说,什么都没做
handle_default_options(options)
# 子命令,也就是 runserver
subcommand = self.argv[1]
# 下面的代码处理子命令
if subcommand == 'help':
...
# help version 的处理,应该就是打印一些信息而已
else:
# 接下来才是真正的业务
# 调用了 fetch_command 方法处理子命令
self.fetch_command(subcommand).run_from_argv(self.argv)
ManagementUtility.fetch_command
def fetch_command(self, subcommand):
# 获取了 app_name,值是固定的 'django.core'
app_name = get_commands()[subcommand]
# get_commands 函数的内容
def get_commands():
if _commands is None:
# 找到 django.core.management.commands 下所有模块
# 每个模块作为 key,值都是 'django.core'
_commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
# 首先加载了 settings
# 虽然只有一句话,内容很多
from django.conf import settings
# django.conf 的 __init__ 文件最后看到:
settings = LazySettings()
# 顾名思义,大概是延时加载了 settings 配置
# 如果 settings 未初始化,获取 os.environ['DJANGO_SETTINGS_MODULE'] 配置
# 在处理 settings 时,会先加载 django.conf.global_settings 的配置
# 然后加载项目的 settings
# 在高版本的 Django 中,配置文件似乎合并了
# 获取 apps,当然这里是空列表
apps = settings.INSTALLED_APPS
# 获取项目目录,语法挺奇怪的
from django.conf import settings
project_directory = setup_environ(
# 为每个 app 找到它的管理模块
# 返回 _commands
# load_command_class 方法
# 返回了 django.core.management.commands.runserver.Command
# 返回 klass
klass = load_command_class(app_name, subcommand)
run_from_argv 方法
# django.core.management.commands.runserver.Command
# 继承 django.core.management.base import BaseCommand
# run_from_argv 也是继承的
def run_from_argv(self, argv):
# 调用 execute
self.execute(*args, **options.__dict__)
def execute(self, *args, **options):
# 调用 handle
# 注意 handle 被重写了
# 调用的是 django.core.management.commands.runserver.Command.handle
output = self.handle(*args, **options)
handle
def handle(self, addrport='', *args, **options):
# addrport 只处理这一个参数,是地址和端口
# 有其他参数将会抛出错误
if args:
raise CommandError('Usage is runserver %s' % self.args)
if not addrport:
addr = ''
port = '8000'
# 还有几个选项,这个开发常用,是否自动重启
use_reloader = options.get('use_reloader', True)
# 定义了一个函数
def inner_run():
# WSGI 处理程序
# WSGIHandler 可调用,是 WSGI 处理程序
# AdminMediaHandler 是对 WSGIHandler 的封装
# AdminMediaHandler 特殊处理媒体文件请求
# AdminMediaHandler 非媒体文件的 HTTP 请求,直接返回 WSGIHandler
handler = AdminMediaHandler(WSGIHandler(), path)
run(addr, int(port), handler)
# run 在 django.core.servers.basehttp.run
# run 启动了 HTTP 服务,这个服务器只能用于开发调试
def run(addr, port, wsgi_handler):
# 绑定地址端口
server_address = (addr, port)
# 生命 WSGIServer 实例,它继承自 HTTPServer
# 请求处理类 WSGIRequestHandler
httpd = WSGIServer(server_address, WSGIRequestHandler)
# 指定应用端,真正处理业务的部分
httpd.set_app(wsgi_handler)
# 监听请求
httpd.serve_forever()
# 如果设置了自动加载,会启动一个线程处理
# 调用该函数
inner_run()
业务细节
AdminMediaHandler 特殊处理了媒体文件,核心依然是 WSGIHandler
django.core.handlers.wsgi.WSGIHandler
class WSGIHandler(base.BaseHandler):
initLock = Lock()
request_class = WSGIRequest
def __call__(self, environ, start_response):
if self._request_middleware is None:
self.initLock.acquire()
# Check that middleware is still uninitialised.
if self._request_middleware is None:
# 加载中间件
# 合法性检查
# 判断是否有中间件的方法
# 如果有,添加在列表中
self.load_middleware()
self.initLock.release()
# 意义不明
set_script_prefix(base.get_script_name(environ))
signals.request_started.send(sender=self.__class__)
# 封装请求
request = self.request_class(environ)
# 业务核心,继承自 base.BaseHandler
response = self.get_response(request)
# 遍历响应中间件
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
# 意义不明
signals.request_finished.send(sender=self.__class__)
django.core.handlers.base.BaseHandler.get_response
def get_response(self, request):
# 使用请求中间件
for middleware_method in self._request_middleware:
response = middleware_method(request)
if response:
return response
# 获取 url 配置,这里获取的是 example.urls
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
# 创建 RegexURLResolver 实例
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
# 根据请求的 url 地址得到对应的 views 函数
callback, callback_args, callback_kwargs = resolver.resolve(
request.path_info)
# 视图中间件
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
return response
# 业务处理
response = callback(request, *callback_args, **callback_kwargs)
# 如果发生异常,应用异常处理中间件
# 在各个步骤中返回 response