zoukankan      html  css  js  c++  java
  • 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
    
  • 相关阅读:
    二叉树前序中序遍历求后序遍历
    二叉树的遍历
    Codeforces Round #381 (Div. 2)
    Codeforces Round #380 (Div. 2, Rated, Based on Technocup 2017
    Codeforces Round #379 (Div. 2)
    HDU 2896 ac自动机裸题
    RabbitMQ镜像模式双节点部署时故障转移过程中队列中消息的状态
    Maven搭建Spring MVC时使用jstl无效
    在使用HttpClient做客户端调用一个API时 模拟并发调用时发生“死锁"?
    使用mongodb提供的dotnet core sdk进行地理位置运算
  • 原文地址:https://www.cnblogs.com/eoalfj/p/11421418.html
Copyright © 2011-2022 走看看