上一篇博客中已经展示了部分路由解析的源码,但是比较大概,不够清晰,只能提供调试的思路。这一篇博客将会抽出路由的每一个组件。详细阐述路由如何解析的。
前面不会连贯起每一个类,因为本篇博客将会是从内向外
URLPattern
URLPattern 最简单,我们先来说说这个,注意,截图中的箭头指向的参数的关系,某一个参数或者指向了一个类,或者指向了另一方法中的参数,表示他们是同一个东西,细细看一下,是可以看懂的。
【1】re_path 函数其实调用的是 _path 函数,_path 通过控制最后一个参数,包装为 re_path 或者 path。而 _path 尝试返回一个 URLPattern 对象或者 URLResolver 对象,其中 URLResolver 对象最后也会解析到一个个的 URLPattern 对象,本次以 URLPattern 作为讲解。
【2】尝试构造一个 Pattern 对象,这个 Pattern 其实是 RegexPattern 对象,因为是传参进来的,所以可以看做被重命名了。构造参数 route 正是 r'^$'
【3】尝试构造一个 URLPattern 对象,他需要的第一参数正是第二步构造的 RegexPattern 对象,第二个参数是【1】中的视图函数,这里形成了一个绑定关系,
【4】其实调用了【5】所在的 match 方法
【5】看着像是正则调用,但是 RegexPattern.regex 怎么会有 search 方法或者 fullmatch 方法,这两个是正则对象,RegexPattern.regex 是一个 LocaleRegexDescriptor 对象。注意:LocaleRegexDescriptor 对象是作为 RegexPattern 的类属性,且 LocaleRegexDescriptor 类中写了一个 __get__
,所以 LocaleRegexDescriptor 其实是一个描述器,描述器的具体用法,请看 这篇博客。self.regex
触发了【6】
【6】instance
指向了当前的 RegexPattern 对象,而 self.attr
则在 LocaleRegexDescriptor 作为类属性初始化的时候就传进去了,当前为 _regex
。于是这里就是尝试从当前的 RegexPattern 对象中获取 _regex
这个对象,有吗?请看 RegexPattern 的初始化方法。此时 regex 的值为 r'^$'
,于是触发了【7】的执行
【7】尝试调用 RegexPattern._compile 方法,并将结果添加给当前的 RegexPattern 实例作为属性,这样做的好处是实现了懒加载,非必要不生成对象。
RegexPattern._compile
这里返回了一个正则对象,所以正则对象就是这么来的。现在 self.regex 就有了 fullmatch 方法和 search 方法。
所以 RegexPattern.match 方法尝试获取一个 match 对象,也就是正则对象对路由字符串的解析结果,在 RegexPattern.match 的后半段,将正则对象的解析结果提取出来作为视图函数的参数。
URLPattern 有两个重要的参数
【1】pattern 用来校验url和当前的正在比较的路由是否匹配,并且提取请求参数
【2】当前路由绑定的视图函数
URLResolver
URLResolver 一般用来接收一个 include 函数的返回值。
include 的返回值是一个三元组,因此可以看做 URLResolver 存储的就是一个列表,里面的每一个元素可以是 URLResolver[存在子应用],也可以是 URLPattern。在这里我们假设列表中每一个元素都是 URLPattern 的实例。对于列表中存在 URLResolver 的实例的情况,可以类推。
URLResolver 跟 URLPattern 很类似,他也需要接收一个正则表达式作为 pattern, 通常是当前应用的开头,用于从上一级路由跳到当前路由。URLResolver 还存放了下一层路由,也就是子应用中的列表。这个 include 已经完成了导入操作。所以这里存放的其实就是个列表。
【1】尝试调用 RegexPattern.match 方法
【2】同上半部讲的一样,通过描述器加反射构造了一个正则对象,于是可以调用 fullmatch 方法或者 search 方法。
【3】这里的代码比较重点,将当前的正则字符串从 url 中去掉了,方便拿到下一级路由去匹配。需要注意的是 new_path URLPattern 的resolve 方法中被丢弃了,因为如果当前是 URLPattern 调用了 resolve 方法,说明将要提取视图函数了。同时从 url 中提取查询参数
【4】对 RegexPattern.match 的返回值进行解包。
【5】在 for 循环内,对每一个列表的元素尝试匹配请求,这时传入的是 new_path,也就是去掉了当前的 URLResolver 的匹配字符串。到了【5】这里就已经跟上半篇博客一样了。
需要注意的是:在 URLResolver.resolve 的循环内部,如果匹配到了路由就不会向下走了,他不会检查所有能匹配上的路由。如果他没有匹配上的话,他会记录当前已经匹配过得路由,用于返回给前端,是否路由输入错了。
URLResolver 和 URLPattern 配合使用,URLPattern 用于最终匹配到路由,并且提取出视图函数,URLResolver 用于跳到下一层路由或者扫描当前所有的元素。
标签:06,URLPattern,URLResolver,对象,RegexPattern,详解,path,路由 From: https://www.cnblogs.com/yaowy001/p/17062980.html