文档章节

【Django源码浅析】—Django runserver启动流程与URL路由

Archer弓兵
 Archer弓兵
发布于 2017/05/25 16:26
字数 1120
阅读 245
收藏 3

新的一篇,大概梳理一下django的url路由流程与runserver的启动流程。

小公举小姐姐们,求赞求转发,要抱抱

分析代码首先找到分析入口,根据由入口开始的处理流程,定位到要分析的节点。

一般调试运行django使用manager命令:

根据上一回的django命令系统的分析

找到runserver命令(django/core/management/commands/runserver.py)

runserver命令调用链为self.handle->self.run->self.inner_run->run

run函数(servers/basehttp.py):

分析run函数,定义了一个httpd_cls类,继承WSGIServer以及ThreadingMinIn,之后初始化,调用serve_forever函数开始监听端口。

监听端口接收请求之后会交由_handle_request_noblock函数处理,最终请求会发送到上图run函数中的WSGIRequestHandler类处理:

RequestHandlerClass就是run方法中的WSGIRequestHandler,WSGIRequestHandler的基类__init__函数会自动调用self.handle方法。

那接着来看一下self.handle:

self.handle调用ServerHandler的run方法,self.server.get_app()得到的就是basehttp.py 中run函数中httpd.set_app(wsgi_handler)中的wsgi_handler,即core/wsig.py中的get_wsig_application函数(此时执行django.setup函数,做app加载),见下图

至此python manager.py runserver 0:80命令启动server的工作基本完成了。在server接到request请求之后会自动将请求交给WSGIHandle类(注:根据wsgi协议,WSGIHandler是callable的,WSGIHandler在初始化的时候执行了load_middleware函数,加载中间件)处理。通过WSGIHandle的get_response函数相应request请求。

def get_response(self, request):
    """Return an HttpResponse object for the given HttpRequest."""
    # Setup default url resolver for this thread
    set_urlconf(settings.ROOT_URLCONF)

    response = self._middleware_chain(request)

    # This block is only needed for legacy MIDDLEWARE_CLASSES; if
    # MIDDLEWARE is used, self._response_middleware will be empty.
    try:
        # Apply response middleware, regardless of the response
        for middleware_method in self._response_middleware:
            response = middleware_method(request, response)
            # Complain if the response middleware returned None (a common error).
            if response is None:
                raise ValueError(
                    "%s.process_response didn't return an "
                    "HttpResponse object. It returned None instead."
                    % (middleware_method.__self__.__class__.__name__))
    except Exception:  # Any exception should be gathered and handled
        signals.got_request_exception.send(sender=self.__class__, request=request)
        response = self.handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())

    response._closable_objects.append(request)

    # If the exception handler returns a TemplateResponse that has not
    # been rendered, force it to be rendered.
    if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
        response = response.render()

    if response.status_code == 404:
        logger.warning(
            'Not Found: %s', request.path,
            extra={'status_code': 404, 'request': request},
        )

    return response

get_response函数第一行就开始加载setting中的urlconfig,紧接着调用

response = self._middleware_chain(request)处理请求,所以可见,相应request请求的诸多操作都是经由一个个middleware处理之后的来的,那么猜一下就知道,加载url路由响应请求自然应该是在加载middleware的操作中完成的了。跟踪代码找到加载middleware函数的位置,最后定位到WSGIHandler的_get_response函数,可以发现其中的这几行代码:

if hasattr(request, 'urlconf'):
    urlconf = request.urlconf
    set_urlconf(urlconf)
    resolver = get_resolver(urlconf)
else:
    resolver = get_resolver()

resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match

往后继续读不难发现,callback函数就是我们在view模块中定义的响应函数,用来相应客户端的请求。所以自然可以推断出get_resolver()函数是获取路由解析类的,而resolver_match则是根据用户请求获取的路由结果,callback则是针对request.path_info的相应函数。

路由加载:

@lru_cache.lru_cache(maxsize=None)
def get_resolver(urlconf=None):
    if urlconf is None:
        from django.conf import settings
        urlconf = settings.ROOT_URLCONF
    return RegexURLResolver(r'^/', urlconf)

路由:

def resolve(self, path):
    path = force_text(path)  # path may be a reverse_lazy object
    tried = []
    match = self.regex.search(path)
    if match:
        new_path = path[match.end():]
        for pattern in self.url_patterns:
            try:
                sub_match = pattern.resolve(new_path)
            except Resolver404 as e:
                sub_tried = e.args[0].get('tried')
                if sub_tried is not None:
                    tried.extend([pattern] + t for t in sub_tried)
                else:
                    tried.append([pattern])
            else:
                if sub_match:
                    # Merge captured arguments in match with submatch
                    sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
                    sub_match_dict.update(sub_match.kwargs)

                    # If there are *any* named groups, ignore all non-named groups.
                    # Otherwise, pass all non-named arguments as positional arguments.
                    sub_match_args = sub_match.args
                    if not sub_match_dict:
                        sub_match_args = match.groups() + sub_match.args

                    return ResolverMatch(
                        sub_match.func,
                        sub_match_args,
                        sub_match_dict,
                        sub_match.url_name,
                        [self.app_name] + sub_match.app_names,
                        [self.namespace] + sub_match.namespaces,
                    )
                tried.append([pattern])
        raise Resolver404({'tried': tried, 'path': new_path})
    raise Resolver404({'path': path})

resolve函数解析request.path_info请求,match为匹配结果,如果匹配失败则抛出404异常,如果匹配成功则进行下一步的url解析,匹配剩余url字符(new_path = path[match.end():]),之后的代码为类似循环嵌套调用(for pattern in self.url_patterns),不断尝试匹配url_patterns中的正则规则,如果最终没有匹配项,抛出404异常,如果最终匹配成功,则返回ResolverMatch实例:

注意:self.url_patterns可能是两种值,一种是RegexURLResolver,另外一种是RegexURLPattern。

if sub_match:
    # Merge captured arguments in match with submatch
    sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
    sub_match_dict.update(sub_match.kwargs)

    # If there are *any* named groups, ignore all non-named groups.
    # Otherwise, pass all non-named arguments as positional arguments.
    sub_match_args = sub_match.args
    if not sub_match_dict:
        sub_match_args = match.groups() + sub_match.args

    return ResolverMatch(
        sub_match.func,
        sub_match_args,
        sub_match_dict,
        sub_match.url_name,
        [self.app_name] + sub_match.app_names,
        [self.namespace] + sub_match.namespaces,
    )

ResolverMatch中的sub_match.func即为view层的响应函数,至此路由完成。

 

© 著作权归作者所有

Archer弓兵
粉丝 5
博文 34
码字总数 14928
作品 0
杭州
程序员
私信 提问
加载中

评论(4)

caol
caol
不错,不错
Archer弓兵
Archer弓兵 博主

引用来自“听风的小猪”的评论

看样我的技术不行,看着有点晕晕的

回复@听风的小猪 : 写得不太好,边想边写的,有机会我会再画一下UML的类图应该就会清晰一点
听风的小猪
听风的小猪
看样我的技术不行,看着有点晕晕的
Archer弓兵
Archer弓兵 博主
居然没人看
利用Django实现一个博客(附全部源码)

不论什么语言,学Web开发必做的项目——个人博客。 本次项目基于Python的知名Web框架Django,从数据库到视图逻辑、再到模板语法,完整的走了一遍MTV开发流程。 markdown非常适合写博客、新闻...

天涯笨熊
2017/12/14
0
0
基于Django 文档1.11 自解+补完 学习django ---part1

学习django 很长一段时间了 国内的资料少之甚少,加之英文文档sei也不愿意看或者看不懂,使得学习更加陡峭,so准备研究官网案例结合我的学习经历,进行姿势补全 *Tip:本文基于Django文档1.1...

xh21bao
2017/04/21
0
0
Python全栈 Web(Django框架、概述、安装)

Django 框架 WEB 与 服务器 WEB : 表示用户可以浏览的网页内容(HTML,CSS,JS) 服务器 专门给用户提供服务的一台机器 1.硬件与软件 硬件范畴:一台机器 软件范畴:一个能够接受用户请求并给出响应...

巴黎香榭
2018/09/19
0
0
轻量级django阅读笔记:最小的django应用

Intro 找不到工作十分难受,在家看书,恰巧翻到这本《轻量级 Django》,看起来还蛮有意思的,做个读书笔记。 1. 最小的 Django App Django 是个重量级框架,所谓最小指的是写最少的代码,理解...

御坂网络路由器
03/03
0
0
CentOS下安装Django

从django的官网上下载django的源码。官网地址:https://www.djangoproject.com/ 安装完成后

动听的椰子
2016/06/02
19
0

没有更多内容

加载失败,请刷新页面

加载更多

非webpack require.js + vue + vueRouter + iView 实现按需加载

适合一个人开发的时候,在整个php框架下,又想单页,又可以直接后端assign变量穿透到模板。又不想写接口搞前后分离脚手架一大堆npm 包, 在php模板下 引入require.js <!DOCTYPE html><html...

一箭落旄头
13分钟前
6
0
新特性解读 | MySQL 8.0 窗口函数详解

原创作者: 杨涛涛 背景 一直以来,MySQL 只有针对聚合函数的汇总类功能,比如MAX, AVG 等,没有从 SQL 层针对聚合类每组展开处理的功能。不过 MySQL 开放了 UDF 接口,可以用 C 来自己写UDF...

爱可生
19分钟前
2
0
23.5 jumpserver介绍

23.5 jumpserver介绍 开源堡垒机jumpserver介绍: 官网www.jumpserver.org Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理 Auth...

oschina130111
25分钟前
3
0
Spring Cloud 入门教程(七): 消息总线(Spring Cloud Bus)(Greenwich.RELEASE)

参考网址:https://blog.csdn.net/forezp/article/details/81041062,由于此文中作者基于git和rabbitMq,为了适应内网我改造为基于mysql和kafka 一、准备工作 1、安装kafka 参考这个:kafka...

pipi1919
26分钟前
1
0
用人工智能改变企业与客户的连接方式

  随着以AI人工智能技术为主的新一代信息技术的快速商业化落地,我国的数字经济正高歌猛进。2017年我国数字经济对GDP的贡献率为55%,接近甚至超越了某些发达国家水平,2018年我国数字经济规...

琴殇的
30分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部