django 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