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转换摩斯密码示例
Feb 16 Python
Python去除字符串前后空格的几种方法
Mar 04 Python
python hough变换检测直线的实现方法
Jul 12 Python
Django密码系统实现过程详解
Jul 19 Python
使用python实现unix2dos和dos2unix命令的例子
Aug 13 Python
python实现查找所有程序的安装信息
Feb 18 Python
Python关于__name__属性的含义和作用详解
Feb 19 Python
mac 上配置Pycharm连接远程服务器并实现使用远程服务器Python解释器的方法
Mar 19 Python
Django Model层F,Q对象和聚合函数原理解析
Nov 12 Python
python读取图片颜色值并生成excel像素画的方法实例
Feb 19 Python
python实现简单倒计时功能
Apr 21 Python
Python中生成随机数据安全性、多功能性、用途和速度方面进行比较
Apr 14 Python
Python实现排序方法常见的四种
Jul 15 #Python
手把手教你使用TensorFlow2实现RNN
一篇文章弄懂Python关键字、标识符和变量
python开发飞机大战游戏
详解Python中下划线的5种含义
Python操作CSV格式文件的方法大全
openstack中的rpc远程调用的方法
You might like
PHP 面向对象程序设计(oop)学习笔记(三) - 单例模式和工厂模式
2014/06/12 PHP
php基础设计模式大全(注册树模式、工厂模式、单列模式)
2015/08/31 PHP
谈谈 PHP7新增功能
2015/12/16 PHP
浅谈PHP安全防护之Web攻击
2017/01/03 PHP
JS+PHP实现用户输入数字后显示最大的值及所在位置
2017/06/19 PHP
基于PHP实现用户登录注册功能的详细教程
2020/08/04 PHP
javascript 必知必会之closure
2009/09/21 Javascript
玩转jQuery按钮 请告诉我你最喜欢哪些?
2012/01/08 Javascript
基于socket.io和node.js搭建即时通信系统
2014/07/30 Javascript
究竟什么是Node.js?Node.js有什么好处?
2015/05/29 Javascript
解决jQuery ajax请求在IE6中莫名中断的问题
2016/06/20 Javascript
jQuery实现的简单悬浮层功能完整实例
2017/01/23 Javascript
Angular.JS中的this指向详解
2017/05/17 Javascript
webpack4 + react 搭建多页面应用示例
2018/08/03 Javascript
使用iView Upload 组件实现手动上传图片的示例代码
2018/10/01 Javascript
关于React动态加载路由处理的相关问题
2019/01/07 Javascript
Vue路由之JWT身份认证的实现方法
2019/08/26 Javascript
jquery实现加载更多&quot;转圈圈&quot;效果(示例代码)
2020/11/09 jQuery
[03:47]2015国际邀请赛第三日现场精彩回顾
2015/08/08 DOTA
[01:00:17]DOTA2-DPC中国联赛 正赛 SAG vs Dynasty BO3 第二场 1月25日
2021/03/11 DOTA
python实现复制整个目录的方法
2015/05/12 Python
Python使用reportlab将目录下所有的文本文件打印成pdf的方法
2015/05/20 Python
python解析基于xml格式的日志文件
2017/02/25 Python
关于python多重赋值的小问题
2019/04/17 Python
Python实现字符型图片验证码识别完整过程详解
2019/05/10 Python
python中的RSA加密与解密实例解析
2019/11/18 Python
Python操作Jira库常用方法解析
2020/04/10 Python
Python实现密钥密码(加解密)实例详解
2020/04/26 Python
Pycharm2020最新激活码|永久激活(附最新激活码和插件的详细教程)
2020/09/29 Python
HTML5 Canvas 绘图——使用 Canvas 绘制图形图文教程 使用html5 canvas 绘制精美的图
2015/08/31 HTML / CSS
海蓝之谜(LA MER)澳大利亚官方商城:全球高端奢华护肤品牌
2017/10/27 全球购物
初中数学教学随笔
2015/08/15 职场文书
想创业成功,需要掌握这些要点
2019/12/06 职场文书
Python使用random模块实现掷骰子游戏的示例代码
2021/04/29 Python
Python 用户输入和while循环的操作
2021/05/23 Python
深入讲解数据库中Decimal类型的使用以及实现方法
2022/02/15 MySQL