Django路由层如何获取正确的url


Posted in Python onJuly 15, 2021
目录
  • 前言
  • Tips - django版本区别
  • 路由匹配
  • 无名分组&有名分组
    • 无名分组
    • 有名分组
    • 小提示
  • 反向解析
    • 路由不涉及分组的反向解析
    • 有名分组&无名分组的反向解析
  • 路由分发

 

前言

客户端浏览器访问Django后端时通过网关和中间件之后会首先在路由层进行路由匹配,只有路由匹配成功之后才能执行对应的视图函数内的逻辑进行数据的处理,本文就来介绍路由层(以diango1.x版本为例)是如何进行路由匹配的?

 

Tips - django版本区别

在django1.x版本和django2.x及更高版本之间有些许不同,不同点之一就是路由层的路由表达式,路由表达式之间的不同具体如下述表格:

 

区别 django1.x django2.x or 3.x
方法 url方法from django.conf.urls import url path方法from django.urls import path
url参数 第一个参数支持正则表达式 第一个参数不支持正则表达式

如果url参数习惯使用正则表达式,2.x和3.x版本的django也提供了另一个方法re_path,该方法就等价于django1.x版本中的path。

# django2.x版本的urls.py
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index',views.index),
    re_path('^index/\d+',views.index),
]

 

路由匹配

这里我们以django1.x版本进行说明django如何进行路由匹配?django1.x版本中路由与视图的对应关系是通过url方法实现的,而url方法的第一个参数url的正则表达式,只要客户端浏览器访问的url能够和某一个路由成功匹配,就会立刻停止继续匹配之后的路由,直接执行第一个匹配到的视图函数,这样就会产生一个问题,如下述代码:

urlpatterns = [
    url(r'test',views.test),
    url(r'testadd',views.testadd),
]

# 127.0.0.1:8080/testadd 会直接和第一个路由匹配上,永远运行不了下面testadd页面

如何解决上述问题呢?可以指定路由的正则表达式必须以什么开始以什么结尾,并且正则表达式不能为空,否则会匹配所有的url,导致后面的页面无法访问,因此使用正则表达式的url时可以采用下述解决方式:

urlpatterns = [
    # 首页,正则表达式不能写空,否则会匹配所有的url后缀,而导致后面的页面无法访问
    url(r'^$',views.home),
 # ^是指匹配的字符必须以什么开始 $是指匹配的字符必须以什么结尾
    url(r'^test/$',views.test),
    url(r'testadd/',views.testadd),
]

 

无名分组&有名分组

首先来看什么分组?分组的意思简单来讲就是给某一段正则表达式用小括号括起来。无名分组的意思简单理解就是分组之后的正则表达式没有名字而有名分组就是分组之后正则表达式有名字。~真是深刻的理解。。。

 

无名分组

无名分组会将分组后括号内的正则表达式匹配到的内容当做位置参数传递给对应的视图函数。

# urls.py
urlpatterns = [
    url(r'test/(\d+)', views.test),   # \d+表示匹配数字
]

# views.py
def test(request, xx):  #  形参xx可以是任意的
    print(xx)
    return HttpResponse('test')

如果在浏览器中访问127.0.0.1:8000/test/100(数字可以是随意的),在pycharm的终端中就会输出100,如果在视图函数test中不增加形参xx就会报错。报错信息如下:

TypeError: test() takes 1 positional argument but 2 were given

    翻译为test函数只有一个形参但是却给了两个实参,因此必须增加一个形参来接收另一个实参。而另一个实参就是无名分组中的正则表达式匹配到的内容。

 

有名分组

就是给被分组了的正则表达式起一个别名,将括号内正则表达式匹配到的内容当作关键字参数传递给对应的视图函数。

# urls.py
urlpatterns = [
    url(r'test/(?P<id>\d+)',views.test),   # \d+表示匹配数字, id就是分组的正则表达式的名字
]

# views.py
def test(request, id):  # 使用有名分组时,视图函数的形参名字必须与有名分组的名字一致
    print(id)
    return HttpResponse('xx')

如果在浏览器中访问127.0.0.1:8000/test/100(数字可以是随意的),在pycharm的终端中就会输出100,如果在视图函数test中形参名字与有名分组的名字不一致,则会报错,报错信息如下:

TypeError: test() got an unexpected keyword argument 'id'

翻译为test函数得到了一个它不需要的关键字参数id。因此使用有名分组时视图函数的形参必须和有名分组的名字一致。

 

小提示

有名分组和无名分组不能同时使用,但是每一种分组可以重复使用多次,同时在视图函数中必须有对应数量的形参进行值的接收。

url(r'test/(\d+)/(\d+)/(\d+)',views.test)
url(r'test/(?P<id1>\d+)/(?P<id2>\d+)/(?P<id3>\d+)', views.test)

 

反向解析

前端浏览器发送过来一条url请求,该url会匹配到一个负责该请求的视图函数(可能同时给视图函数提供一些传参),此为正向匹配。
从视图函数绑定关系的别名出发(可能需要一些参数),寻找一条完整url的过程是反向,所谓解析就是通过别名(或者说是url匹配关系的别名,又或者url-pattern的别名)外加一些参数,获取一条完整的url。

正向匹配: url                 -------------------------------->    视图函数(+参数)
反向解析:别名(参数)  ---------------------------------->   url

使用反向解析的目的就是在前端HTML页面中更加方便的获取一条url,避免硬编码减少程序维护的复杂度。那么如何使用反向解析呢?使用反向解析分为两步:
①在路由匹配文件urls.py中为路由设置别名;
②在视图函数或者在HTML页面中使用别名。

使用反向解析也分为两种情况,一种是路由不涉及分组的情况,另一种就是有名分组和无名分组的反向解析。

 

路由不涉及分组的反向解析

首先需要在urls.py为路由和视图函数的对应关系设置别名,代码如下:

urlpatterns = [
    re_path('index/', views.index, name='index'),
    re_path('test/', views.test, name='test') # 路由与视图函数的对应关系别名name为test, 可以是任意的,但是必须唯一
]

设置好路由与视图函数的对应关系的别名之后就可以在后端或者前端HTML页面进行反向解析了,通过别名获取url。

# views.py - 在后端视图函数中反向解析,需要借助模块实现动态解析
from django.shortcuts import render, redirect, HttpResponse, reverse


# Create your views here.
def index(request):
    return HttpResponse('index')


def test(request):
    return redirect(reverse('index'))

上述代码当访问127.0.0.1:8000/test/时就会通过test函数重定向,而重定向的url就是通过reverse方法进行反向解析得到的index/路由。

当然在前端HTML页面上也可以通过模板语法进行反向解析的操作,同样是通过别名找到对应关系解析出url后执行对应的视图函数。

# views.py
from django.shortcuts import render, redirect, HttpResponse


# Create your views here.
def index(request):
    return HttpResponse('index')


def test(request):
    return render(request, 'render_html.html')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href={% url 'index' %}>click me</a>  <!--通过{% url '别名' %}的语法格式对后端的别名进行解析,点击即可跳转到index/路由-->
</body>
</html>

 

有名分组&无名分组的反向解析

有名分组和无名分组的反向解析与不分组时有一些不同,有名分组和无名分组反向解析在url.py中的设置和没有分组时的设置操作是一致的,都是通过参数name为路由和视图函数的对应关系起一个别名,但是在存在分组的情况下反向解析时不仅要提供别名还需要路由正则表达式分组中需要的数据,有名分组时反向解析时提供数据的方式不论是在前端还是后端都有两种方式,其中一种是有名分组和无名共有的方式。

首先看无名分组的反向解析:

# urls.py
urlpatterns = [
    re_path('index/(\d+)', views.index, name='index'),
    re_path('test/', views.test, name='test')
]

-----------------------------------------无名分组后端反向解析-------------------------------
# views.py  - 后端的反向解析
def index(request, x):
    return HttpResponse('index')

def test(request):
    # 参数必须是以元组的形式,并且参数必须能够和正则表达式中的分组部分匹配,否则会报错,Reverse for 'func' with no arguments not found. 1 pattern(s) tried: ['index/(\\d+)']
    return redirect(reverse(viewname='index', args=(1,))) 


-----------------------------------------无名分组前端反向解析--------------------------------
# views.py
def index(request, x):
    return HttpResponse('index')

def test(request):
    return render(request, 'render_html.html')

# render_html.html
<body>
<a href={% url 'index' 1 %}>click me</a>   # {% url 别名 分组匹配的参数 %}
</body>

下面再来看有名分组的方向解析,有名分组的反向解析有两种实现方式,第一种与无名分组一致,另一种代码如下:

# urls.py
urlpatterns = [
    re_path('index/(?P<id>\d+)', views.index, name='index'),
    re_path('test/', views.test, name='test')
]

----------------------------------------有名分组反向解析 - 后端反向解析-----------------------
# views.py
def index(request, id):
    return HttpResponse('index')

def test(request):
    # 匹配有名分组的参数是字典的格式字典的key就是有名分组的名字
    return redirect(reverse(viewname='index', kwargs={'id': 2}))

--------------------------------------有名分组反向解析 - 前端反向解析-------------------------
# views.py
def index(request, id):
    return HttpResponse('index')

def test(request):
    return render(request, 'render_html.html')

# render_html.html
<body>
<a href={% url 'index' id=2 %}>click me</a>  # {% url 别名 有名分组名字=分组匹配的参数%} 
</body>

 

路由分发

django每一个应用都可以有自己的urls.py/templates文件夹/static文件夹,基于这一点django可以非常好的实现分组开发,每个人只写自己负责的应用部分即可,那么又如何将不同的应用整合到一起呢?只需要将所有的应用复制到一个新的django项目中(git协同开发后期再讲...)然后在配置文件中注册所有的应用最后利用路由分发将所有应用整合,**路由分发就是识别当前url属于哪个应用下的,然后直接分发给对应的应用再做进一步的处理。**使用路由分发需要在每个应用下创建urls.py称为子路由,原本的urls.py称为总路由,比如说一个django项目中创建了两个应用分别是first和second,路由分发可以通过如下方式实现:

----------------------------子路由文件---------------------------------------------------
# first应用下的urls.py - first_django/first/urls.py
from django.conf.urls import url
from first import views

urlpatterns = [
    url(r'^index/', views.index),
    url(r'^test/', views.test),
]

# second应用下的urls.py - first_django/second/urls.py
from django.conf.urls import url
from second import views

urlpatterns = [
    url(r'^index/', views.index),
    url(r'^test/', views.test),
]

-----------------------------------------总路由文件--------------------------------------
# first_django/first_django/urls.py
from django.conf.urls import url,include
from django.contrib import admin
from firstp import urls as first_url
from second import urls as second_url

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^first/',include(first_url)),
    url(r'^second/',include(second_url))
]

使用路由分发之后,访问不同的应用下的url路由中必须表示该路由属于哪个应用,比如访问127.0.0.1:8000/first/test,表示先通过first到达总路由进行路由分发然后在first应用中在进行test/部分的匹配。总路由做路由分发时url()的正则表达式参数不能以$结尾,必须以/结尾。

上述总路由文件还有一种简化版的代码,无需导入子路由,直接include子路由字符串,如下:

-----------------------------------------总路由文件--------------------------------------
# first_django/first_django/urls.py
from django.conf.urls import url,include
from django.contrib import admin
# from firstp import urls as first_url
# from second import urls as second_url

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^first/',include('first.urls')),
    url(r'^second/',include('first.urls'))
]

到此这篇关于Django路由层如何获取正确的url的文章就介绍到这了,更多相关Django路由层获取url内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
使用django-suit为django 1.7 admin后台添加模板
Nov 18 Python
浅析Python中的getattr(),setattr(),delattr(),hasattr()
Jun 14 Python
酷! 程序员用Python带你玩转冲顶大会
Jan 17 Python
numpy返回array中元素的index方法
Jun 27 Python
详解PyCharm安装MicroPython插件的教程
Jun 24 Python
使用Python画股票的K线图的方法步骤
Jun 28 Python
Python列表删除元素del、pop()和remove()的区别小结
Sep 11 Python
Python pip配置国内源的方法
Feb 14 Python
pyinstaller将含有多个py文件的python程序做成exe
Apr 29 Python
浅谈keras中的目标函数和优化函数MSE用法
Jun 10 Python
python实现猜拳游戏项目
Nov 30 Python
Python+OpenCV实现在图像上绘制矩形
Mar 21 Python
Python实现排序方法常见的四种
Jul 15 #Python
手把手教你使用TensorFlow2实现RNN
一篇文章弄懂Python关键字、标识符和变量
python开发飞机大战游戏
详解Python中下划线的5种含义
Python操作CSV格式文件的方法大全
openstack中的rpc远程调用的方法
You might like
细谈php中SQL注入攻击与XSS攻击
2012/06/10 PHP
PHP计算一年多少个星期和每周的开始和结束日期
2014/07/01 PHP
总结PHP如何获取当前主机、域名、网址、路径、端口和参数等
2016/09/09 PHP
phpStudy中升级MySQL版本到5.7.17的方法步骤
2017/08/03 PHP
php获取微信基础接口凭证Access_token
2018/08/23 PHP
jQuery Selector选择器小结
2010/05/06 Javascript
javascript 模式设计之工厂模式详细说明
2010/05/10 Javascript
JS实现往下不断流动网页背景的方法
2015/02/27 Javascript
JQuery通过AJAX从后台获取信息显示在表格上并支持行选中
2015/09/15 Javascript
JavaScript 闭包机制详解及实例代码
2016/10/10 Javascript
js实现简单的计算器功能
2017/01/16 Javascript
常用的几个JQuery代码片段
2017/03/13 Javascript
详解用webpack的CommonsChunkPlugin提取公共代码的3种方式
2017/11/09 Javascript
关于react中组件通信的几种方式详解
2017/12/10 Javascript
Vue.js实现备忘录功能
2019/06/26 Javascript
javascript写一个ajax自动拦截并下载数据代码实例
2019/09/07 Javascript
npm 语义版本控制详解
2019/09/10 Javascript
快速了解Vue父子组件传值以及父调子方法、子调父方法
2020/07/15 Javascript
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
python中__call__方法示例分析
2014/10/11 Python
Python中的filter()函数的用法
2015/04/27 Python
python实现决策树
2017/12/21 Python
python并发编程多进程 互斥锁原理解析
2019/08/20 Python
Python3实现打印任意宽度的菱形代码
2020/04/12 Python
使用Dajngo 通过代码添加xadmin用户和权限(组)
2020/07/03 Python
通过python-pptx模块操作ppt文件的方法
2020/12/26 Python
CSS3 渐变(Gradients)之CSS3 径向渐变
2016/07/08 HTML / CSS
利用HTML5+CSS3实现3D转换效果实例详解
2017/05/02 HTML / CSS
Michael Kors加拿大官网:购买设计师手袋、手表、鞋子、服装等
2019/03/16 全球购物
优秀本科生求职推荐信
2014/02/24 职场文书
国培远程培训感言
2014/03/08 职场文书
项目经理任命书
2014/06/04 职场文书
个人授权委托书
2014/09/15 职场文书
党的群众路线教育实践活动对照检查材料(个人)
2014/09/24 职场文书
酒店工程部岗位职责
2015/02/12 职场文书
民主评议教师党员自我评价
2015/03/04 职场文书