Django学习之十一:真正理解Django的路由分发和反解url原理

目录

  • URL Dispatcher
    • 简介
    • 模式概念
    • 对比URLPattern 与 URLResolver (多态的体现)
    • 构建子路由几种方式
    • 反解url算法逻辑

URL Dispatcher

简介

django的url dispatcher 设计是基于一个url mapper来工作的。
这个url mapper主要用在两个方向:

  1. url 匹配到 视图
  2. 通过提供的标识,反解出url

    Django provides a solution such that the URL mapper is the only repository of the URL design. You feed it with your URLconf and then it can be used in both directions:
             ** Starting with a URL requested by the user/browser, it calls the right Django view providing any arguments it might need with their values as extracted from the URL.
             ** Starting with the identification of the corresponding Django view plus the values of arguments that would be passed to it, obtain the associated URL.

模式概念

Django的URL 模式非常的清晰和优雅。一个高质量的web应用就需要一个好的URL模式。
Django的URL 助记点:

  • 依照MVC模式,通过url 分发到 对应的 view视图
  • 将 url 和 view视图都封装到了URLPattern对象,统称url对象
  • url对象放到urlpattern列表中
  • urlpattern列表单独放在一个module中,我们叫url module。一般命名上都叫urls.py
  • 每一个django项目,都有一个唯一的叫root_urlconf的url module.这个ROOT_URLCONF时可以配置的放在项目的settings.py中。指定模块路径相对于项目的python path 路径字符串即可,如'luffyapi.urls'
  • 也可以通过中间件对HTTPRequest对象添加一个属性叫urlconf,赋值指定url module,这样就会使用HttpRequest.urlconf 作为root_urlconf,针对当前request的生命周期。
  • 中间件还是什么时候初始化加载url module
  • 按着列表顺序,第一个匹配到的就停止匹配了。然后import and call view
  • url对象不仅提供通过url匹配拿到view,还提供通过名称拿到url字符串,这就是所谓的反解析url。反解url主要用在重定向响应或者html模板中。还有就是model object定义一个get_absolute_url()对象方法中。
  • url对象名称,通过url对象实例化参数中指定,re_path(r'test',test_view, name='testurl')'
  • 还有一个 URLResolver对象,这个对象是urlpattern对象的容器。且URLResolver对象可以嵌套,也就是URLResolver对象看成URLPattern和URLResolver的容器,容器中放置一个URLResolver对象,就是路由的嵌套,也就是子路由。最顶层有一个URLResolver对象,即顶层容器。
  • 无论URLPattern对象还是URLResolver对象,都是通过re_path()或者path()得到的。
  • 为了提供效率切不浪费内存空间,每个URLPattern的url正则表达式都是第一次访问时才会编译(python中有正则表达式对象,放于内存中)
  • 判断实例化为URLResolver对象还是URLPattern对象,根据re_path()或者path()的第二个参数的类型。如果时list或者tuple则实例化为URLResolver对象。如果是callable就实例化为URLPattern对象。
  • 所以利用子路由来减少过多url相同前缀的冗余,时最佳实践。就在前面也所过了,子路由也是有URLResolver对象。所以要通过re_path等来实例化出一个子路由,就得完成一个子路由的构建过程。子路由构建过程具体看本文下面。
  • 现在说回url对象反解获取url字符串的功能
  • 对url对象进行命名, 提供实例化时的name参数
  • django-app-namespace, 源码中叫 app_name
  • 由于django项目中,app时可插拔可复用的,所以对同一个app的多次使用,就要通过对其进行区别,所以提出了app instance的概念,通过不同子路由方式来逻辑划分同一个app的场景下,提出了instance namespace。在源码中就叫 namesapce
  • 通过app_name 和 namespace 都可以作为反解url的一个参数
  • 查看from django.urls import reverse 的源码,理解怎么利用 name/app_name/namespcae反解出url对象的实际url字符串的。
  • 反解url还要提供args 或者kwargs 参数。

对比URLPattern 与 URLResolver (多态的体现)

通过对比两个类的定义:

65c17dd5gy1fzohkg9pqdj20w60cqgmp.jpg
65c17dd5gy1fzohlmbq20j216q0viwi0.jpg

看到,urlresolver也有resovle解析方法。只不过urlresolver的解析会再去加载子url module模块中的urlpatterns列表。然后再对列表中的进行循环匹配过程,一直嵌套下去,知道最后的return跳出返回一个ResolverMatch对象。而urlpattern的resolver直接就返回ResovlerMatch对象了。只不过前者会有重新加载获取子url module模块来获取urlpatterns的逻辑。

两个类都用同名的方法,只是表现出来的的状态有所不同。这就是面向对象多态在代码中的体现。提供相同的对外接口,展现出来的状态过程有所不同,最后返回相同的对象。

构建子路由几种方式

子路由除了减少路由前缀的冗余,还可以满足多种url前缀使用同一app的业务场景。

方式一

参照源码,从最low-level源码层面的方式,参照实例化出URLResolver对象的源码

if isinstance(view, (list, tuple)):  # 这里的view是re_path或path的第二个参数# For include(...) processing.pattern = Pattern(route, is_endpoint=False)urlconf_module, app_name, namespace = viewreturn URLResolver(pattern,urlconf_module,kwargs,app_name=app_name,namespace=namespace,)

从源码可以看出,如果view参数是一个列表或元组类型,那么将会实例化出URLResolver对象,并且对view参数要有且有三个元素。第一个元素可以是子路由的模块的python path 也可以直接是 url对象的列表(查看URLResolver.url_patterns源码可以理解);第二个元素和第三个元素都可以空,也可以都有,但是不能只有namespace单独有。

方式二

django内置的from django.urls import include 提供生成第一种方式view参数的函数

include源码:

def include(arg, namespace=None):app_name = Noneif isinstance(arg, tuple):# Callable returning a namespace hint.try:urlconf_module, app_name = argexcept ValueError:if namespace:raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that ''provides a namespace.')raise ImproperlyConfigured('Passing a %d-tuple to include() is not supported. Pass a ''2-tuple containing the list of patterns and app_name, and ''provide the namespace argument to include() instead.' % len(arg))else:# No namespace hint - use manually provided namespace.urlconf_module = argif isinstance(urlconf_module, str):urlconf_module = import_module(urlconf_module)patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)app_name = getattr(urlconf_module, 'app_name', app_name)if namespace and not app_name:raise ImproperlyConfigured('Specifying a namespace in include() without providing an app_name ''is not supported. Set the app_name attribute in the included ''module, or pass a 2-tuple containing the list of patterns and ''app_name instead.',)namespace = namespace or app_name# Make sure the patterns can be iterated through (without this, some# testcases will break).if isinstance(patterns, (list, tuple)):for url_pattern in patterns:pattern = getattr(url_pattern, 'pattern', None)if isinstance(pattern, LocalePrefixPattern):raise ImproperlyConfigured('Using i18n_patterns in an included URLconf is not allowed.')return (urlconf_module, app_name, namespace)

可以看到提供app_name 而不提供namespace的话是会抛出异常的。

Notice:关于app_name 与 namespace 存在这样一个依赖逻辑:

  1. 提供了app_name, 可以不提供namesapce
  2. 提供了namespace,就必须提供app_name
  3. 两者都提供
  4. 两者都不提供
    意思就是有namespace必须有app_name.
    为什么要有这样的逻辑?
    因为这和反解url 算法逻辑有关。看下面说明有关算法逻辑

inlucde()的参数方式,也有几种:

  1. include('luffyapi.urls') # app_name 可能来自'luffyapi.urls.app_name' ,这里没提供namespace,所以'luffyapi.urls'中不能有app_name.'
  2. include(('luffyapi.urls', 'luffyapi')) # app_name 可能被'luffyapi.urls.app_name' 覆盖
  3. include(('luffyapi.urls', 'luffyapi'), namespace='luffyapiuser')
  4. include('luffyapi.urls', namespace='luffyapiuser') # 这种方式在'luffyapi.urls' 中就必须有app_name。

反解url算法逻辑

参考官方文档和from django.urls import reverse 函数的源码。大致可以这样理解:

  1. 首先,如果reverse或者 url tag(in Template file) 中,只是提供了'name' url对象实例化是的name参数,那么反解逻辑很简单.直接循环一个记录字典中找到。对于name相同的,只会取出在urlpattern列表中最后一个。
  2. 如果,提供的反解名字是'namespace:name' 这种模式,逻辑就变得复杂了。
    1.1 首先将namespace 作为一个app_name 查找,会yield 返回这个app_name 的所有instance的列表。
    1.2 然后django会找寻与app_name名字相同的instance namespace作为用于解析name的对象。。
    1.3 如果没有,django会使用最后部署的instance作为解析name的对象。
    1.4 如果列表中一个都没匹配上app_name,那么django会直接通过instanc namespace去查找。
    1.5 最后,如果reverse带入了current_app 参数指定当前的app ,那么就使用当前的URLResolver来解析name。最后这一点有点不好理解特别是在使用reverse与 url tag 上。

转载于:https://www.cnblogs.com/ZJiQi/p/10339006.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412346.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unable to locate tools.jar

初使用ant的时候,打开cmd,使用ant -version查看ant版本以测试ant是否能正常工作, 我先前是已经将ant的bin目录添加进入环境变量中了,后来运行中报了这么一个错误: 解决办法就是将C:\Program Files (x86)\Java\jdk1.6.…

[vue] 你们项目为什么会选vue而不选择其它的框架呢?

[vue] 你们项目为什么会选vue而不选择其它的框架呢? Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API;vue是单页面应用,使页面局部刷新,不用每次跳转页面都要请求所有数据和dom,这…

你所忽略的,覆盖equals时需要注意的事项《effective java》

我们都知道Object的equals的比较其实就是的比较,其实是内存中的存放地址的比较。正常逻辑上:类的每个实例本质上都是唯一的。 在工作中我们实际的业务逻辑往往有可能出现一些相对特殊的需求需要对equals方法进行重写,那么重写equals需要注意哪…

[vue] vue在开发过程中要同时跟N个不同的后端人员联调接口(请求的url不一样)时你该怎么办?

[vue] vue在开发过程中要同时跟N个不同的后端人员联调接口(请求的url不一样)时你该怎么办? devServer中把所有的服务人员的地址代理都写进去, 然后动态更改接口的baseUrl,这样切换不同后端人员的时候不用重启个人简介…

使用 bat 文件管理计算机服务

echo off title 计算机服务管理 :allstart cls echo 曾俊工作室 echo 1.SQL Server 2008 服务开启、关闭 echo 2.MySQL 服务开启、关闭 echo 3.Oracle 11g 服务开启、关闭 echo e.退出 set in set /p in请输入: if "%in%""1" goto sqlserver if "…

处女座与复读机

链接:https://ac.nowcoder.com/acm/contest/327/G来源:牛客网 一天,处女座在牛客算法群里发了一句“我好强啊”,引起无数的复读,可是处女座发现复读之后变成了“处女座好强啊”。处女座经过调查发现群里的复读机都是失…

[vue] 如何解决vue打包vendor过大的问题?

[vue] 如何解决vue打包vendor过大的问题? 1、在webpack.base.conf.js新增externals配置,表示不需要打包的文件,然后在index.html中通过CDN引入externals: {"vue": "Vue","vue-router": "VueRouter"…

jquery ajax 解决跨域访问问题

当使用jquery ajax进行跨域请求时,会出现Access-Control-Allow-Origin错误 //获取验证码 var send_status true; $(#pull_code).click(function () {if (!send_status) {return false;}var phone $(#phone).val();if (!phone) {alert(请输入手机号码!);return fa…

测试网络

|||||106.13.4.129|||||转载于:https://www.cnblogs.com/Sendige/p/10343124.html

bootstrap 一排5个_BootStrap从基础到项目实战_第1季_03章_02_CSS样式栅格系统实例

目标目标一、理解什么是栅格布局目标二、掌握栅格布局具体应用目标三、掌握BootStrap通用CSS样式(排版、代码、代码、表单、按钮、图片、辅助类、响应式工具)内容一、BootStrap全局CSS之 - 栅格系统实例1.1 栅格系统实例实战前的理论准备通过下面的截图可以比较清楚的来查看Boo…

[vue] 在移动端使用vue,你觉得最佳实践有哪些?

[vue] 在移动端使用vue,你觉得最佳实践有哪些? vant,mint,uniapp个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

Spring加载resource时classpath*:与classpath:的区别

https://www.cnblogs.com/yangy608/p/3365539.html Spring可以通过指定classpath*:与classpath:前缀加路径的方式从classpath加载文件,如bean的定义文件.classpath*:的出现是为了从多个jar文件中加载相同的文件.并完成组合 classpath:只能加载找到的第一个文件. classpath*:的使…

光耦驱动单向可控硅_光耦是什麽?

光耦是一种广泛用于电子产品中的元器件,亦称作光电耦合器或是光电隔离器,光耦的动作顺序为一个电→光→电的过程,光耦元件于输入端由电讯号转为光讯号,输出端则吸收光讯号后转换为电流/电压;在实体电路上光耦确实的隔离…

[vue] 说下你的vue项目的目录结构,如果是大型项目你该怎么划分结构和划分组件呢?

[vue] 说下你的vue项目的目录结构,如果是大型项目你该怎么划分结构和划分组件呢? views目录存放一级路由的组件,即视图组件 Components目录存放组件 Store存放vuex相关文件 Router目录存放路由相关文件 Untils目录存放工具js文件 API目录存放…

process launch failed: Security

运行系统:iOS9.1 运行工具:xCode7.1.1 问题描述:在真机iPhone6上运行xCode案例时,会提示“Could not launch “iOSCase” process launch failed: Security” 在iPhone6上也可以看到这样的提示: 解决方法&#xff1a…

单体预聚合的目的是什么_线型低密度聚乙烯的单体单元比例到底是多少?

我国现行法定归类依据关于线型低密度聚乙烯(LLDPE)单体单元比例的规定主要可见于三处:第三十九章总注释:“值得注意的是,商品聚合物有时含有比其缩写名称所述的单体单元要多〔例如,线性低密度聚乙烯(LLDPE)基本上是乙烯聚合物&…

EditPlus软件自动补全文档htmlbar.acp设置 及 模板文件格式

1.在htmlbar.acp文件末尾添加如下内容&#xff0c;可自动补全&#xff1a;#THTML<html>^!</html>#THEAD<head>^!</head>#TTITLE<title>^!</title>#TBODY<body>^!</body>#TLINK<link rel"stylesheet" type"t…

[vue] vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?

[vue] vue要做权限管理该怎么做&#xff1f;如果控制到按钮级别的权限怎么做&#xff1f; 可以通过指令去做 Vue.directive(hasPermission, { bind(el, binding, vnode) { const permissions vnode.context.$store.state.account.permissions if (binding.value ) return co…

未捕获的异常 'NSInternalInconsistencyException'

实现功能 通过下面的代码&#xff0c;需要在iOS设备屏幕的一角中显示文本“hellow xcode”&#xff1a; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// Override point for customization after appli…

银行系统日终结算要多久_美股顽强翻红!两连跌终结,联储降息预期已超九成!制造业疲软消费者信心坚挺,三大股指又假摔?...

美国股市昨日先抑后扬终结两连跌&#xff0c;开启反弹&#xff0c;道指、标普、纳指纷纷翻红。10月3日晚&#xff0c;美东时间周四&#xff0c;美股集体低开&#xff0c;盘初受宏观经济数据不及预期影响&#xff0c;三大指数大幅跳水跌逾1%&#xff0c;道指跌超300点。此后美股…