Django 之 URL dispatcher
阅读目录
URL 调度器(URL dispatcher)
在刚开始接触 django 的时候,我们尝试着从各种入门文档中创建一个自己的 django 项目,需要在 mysite.urls.py 中配置 URL,这是 django url 匹配处理机制的一小部分。
URL 调度器详解
django url 匹配处理机制主要由一下模块实现: django.conf.urls 和 django.core.urlresolver.py. 有需要摘取上一节中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
可以简单的理解为 get_response() 中构造了 RegexURLResolver 对象并调用了 RegexURLResolver.resolve(path) 启动解析. 从上面的代码中, 可以获知 urlconf 默认使用的是 mysite.settings.py 中的 ROOT_URLCONF, 而也确实可以在 mysite.settings.py 中找到对应的设置项, 并且做出修改.
从上, 至少可以知道, 真正发挥匹配作用的是 RegexURLResolver 对象, 并调用 RegexURLResolver.resolve() 启动了解析, 一切从这里开始. 从 urlresolver.py 中抽取主干部分, 可以得到下面的 UML 图:
LocaleRegexProvider 类只为地区化而存在, 他持有 regex 属性, 但在 RegexURLResolver 和 RegexURLPattern 中发挥不同的作用:
- RegexURLResolver: 过滤 url 的前缀, 譬如如果 regex 属性值为 people, 那么能将 people/daoluan/ 过滤为 daoluan/.
- RegexURLPattern: 匹配整个 url.
在展开 ResolverMatch, RegexURLPattern, RegexURLResolver 三个类之前, 暂且将他们理解为:
- ResolverMatch 当匹配成功时会实例化返回
- RegexURLPattern, RegexURLResolver 匹配器, 但有不同.
然后需要先介绍三个函数: url(), include(), patterns(), 三者经常在 urls.py 中用到. 它们在 django.conf.urls 中定义. 摘抄和解析如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
|
可以简单的理解为, url() 根据具体情况返回 RegexURLResolver 或者 RegexURLPattern 对象; patterns() 返回了包含有 RegexURLPattern 和 RegexURLResolver 对象的列表. 当在 urls.py 中出现:
每个 include() 的时候, 最终会产生一个 RegexURLResolver 对象;
否则为 RegexURLPattern 对象.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
|
下面的具体例子将加深对 RegexURLResolver.reslove() 调用的理解. 假设工程名为 mysite, 并且创建了 app people.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
URL 调度过程实例
当访问 http://exapmle.com/people/daoluan/ 的时候 URL dispatcher 的调度过程(蓝色部分):
对应上面的例子 url 调度器机制的具体工作过程如下, 从 BaseHandler.get_response() 开始说起:
1. BaseHandler.get_response() 中根据 settings.py 中的 ROOT_URLCONF 设置选项构造 RegexURLResolver 对象, 并调用 RegexURLResolver.resolve("/people/daoluan/") 启动解析, 其中 RegexURLResolver.regex = "^\", 也就是说它会过滤 "\", url 变为 "people/daoluan/";
2. resolve() 中调用 RegexURLResolver.url_patterns(), 加载了所有的匹配信息如下(和图中一样):
- (类型)RegexURLPattern (正则匹配式)[^$]
- RegexURLPattern [^about/]
- RegexURLResolver [^people/]
- RegexURLPattern [^contact/]
- RegexURLPattern [^update/]
语句 for pattern in self.url_patterns: 开始依次匹配. 第一个因为是 RegexURLPattern 对象, 调用 resolve() 为 RegexURLPattern.resolve(): 它直接用 [^$] 去匹配 "people/daoluan/", 结果当然是不匹配.
3. 下一个 pattern 过程同上.
4. 第三个 pattern 因为是 RegexURLResolver 对象, 所以 resolve() 调用的是 RegexURLResolver.resolve(), 而非上面两个例子中的 RegexURLPattern.resolve(). 因为第三个 pattern.regex = "^people/", 所以会将 "people/daoluan/" 过滤为 "daoluan/". pattern.resolve() 中会调用 RegexURLResolver.url_patterns(), 加载了所有的匹配信息如下(和图中一样):
- RegexURLPattern [^daoluan$]
- RegexURLPattern [^sam$]
- RegexURLPattern [^jenny$]
语句 for pattern in self.url_patterns: 开始依次匹配. 第一个就中, 过程和刚开始的过程一样. 因此构造 RegexURLMatch 对象返回. 于是 BaseHandler.get_response() 就顺利得到 RegexURLMatch 对象, 其中记录了有用的信息. 在 BaseHandler.get_response() 中有足够的信息让你知道开发人员在 views.py 中定义的函数是 def daoluan(request): 在什么时候调用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
总结
从上面知道, url 调度器主要 RegexURLResolver、RegexURLPattern、ResolverMatch 和三个辅助函数 url()、include()、patterns() 完成。
可以发现,url 的调度顺序是根据 urls.py 中的声明顺序决定的,即遍历一张表而已,所以有没有办法提高查找的效率?