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 相关文章推荐
一些Python中的二维数组的操作方法
May 02 Python
如何使用 Pylint 来规范 Python 代码风格(来自IBM)
Apr 06 Python
Python中XlsxWriter模块简介与用法分析
Apr 24 Python
Python+OpenCV采集本地摄像头的视频
Apr 25 Python
Django自定义模板过滤器和标签的实现方法
Aug 21 Python
python实现把二维列表变为一维列表的方法分析
Oct 08 Python
Python猴子补丁知识点总结
Jan 05 Python
python matplotlib:plt.scatter() 大小和颜色参数详解
Apr 14 Python
python,Java,JavaScript实现indexOf
Sep 09 Python
Pycharm 如何设置HTML文件自动补全代码或标签
May 21 Python
Python快速实现一键抠图功能的全过程
Jun 29 Python
Django+Nginx+uWSGI 定时任务的实现方法
Jan 22 Python
Python实现排序方法常见的四种
Jul 15 #Python
手把手教你使用TensorFlow2实现RNN
一篇文章弄懂Python关键字、标识符和变量
python开发飞机大战游戏
详解Python中下划线的5种含义
Python操作CSV格式文件的方法大全
openstack中的rpc远程调用的方法
You might like
随机广告显示(PHP函数)
2006/10/09 PHP
两款万能的php分页类
2015/11/12 PHP
php 变量引用与变量销毁机制详细介绍
2016/12/05 PHP
替换php字符串中的单引号为双引号的方法
2017/02/16 PHP
php解析mht文件转换成html的实例
2017/03/13 PHP
Win7环境下Apache连接MySQL提示连接已重置的解决办法
2017/05/09 PHP
laravel异步监控定时调度器实例详解
2019/06/21 PHP
jQuery读取和设定KindEditor值的方法
2013/11/22 Javascript
JavaScript获取图片的原始尺寸以宽度为例
2014/05/04 Javascript
cocos2dx骨骼动画Armature源码剖析(一)
2015/09/08 Javascript
JavaScript实现自动生成网页元素功能(按钮、文本等)
2015/11/21 Javascript
基于canvas实现的钟摆效果完整实例
2016/01/26 Javascript
jquery.uploadifive插件怎么解决上传限制图片或文件大小问题
2017/05/08 jQuery
vue同步父子组件和异步父子组件的生命周期顺序问题
2018/10/07 Javascript
关于微信公众号开发无法支付的问题解决
2018/12/28 Javascript
puppeteer库入门初探
2019/01/09 Javascript
解决layer.prompt无效的问题
2019/09/24 Javascript
[15:39]教你分分钟做大人:龙骑士
2014/10/30 DOTA
编写Python脚本批量下载DesktopNexus壁纸的教程
2015/05/06 Python
Python格式化输出%s和%d
2018/05/07 Python
Python实现重建二叉树的三种方法详解
2018/06/23 Python
Python3.5集合及其常见运算实例详解
2019/05/01 Python
python求加权平均值的实例(附纯python写法)
2019/08/22 Python
python如何遍历指定路径下所有文件(按按照时间区间检索)
2020/09/14 Python
使用Pytorch搭建模型的步骤
2020/11/16 Python
Pycharm创建文件时自动生成文件头注释(自定义设置作者日期)
2020/11/24 Python
HTML5 本地存储和内容按需加载的思路和方法
2011/04/07 HTML / CSS
幼儿园教师个人反思
2014/01/30 职场文书
2014旅游局领导班子四风问题对照检查材料思想汇报
2014/09/19 职场文书
幼儿园感恩节活动方案
2014/10/06 职场文书
车辆年检委托书范本
2014/10/14 职场文书
交通事故案件代理词
2015/05/23 职场文书
民间借贷借条范本
2015/05/25 职场文书
汽车修理厂管理制度
2015/08/05 职场文书
护士年终工作总结不会写?各科护士模板总结
2020/01/02 职场文书
基于Go语言构建RESTful API服务
2021/07/25 Golang