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实现变量数值交换及判断数组是否含有某个元素的方法
Sep 18 Python
Python中类的初始化特殊方法
Dec 01 Python
Python Requests模拟登录实现图书馆座位自动预约
Apr 27 Python
深入浅析Python中list的复制及深拷贝与浅拷贝
Sep 03 Python
33个Python爬虫项目实战(推荐)
Jul 08 Python
Django 反向生成url实例详解
Jul 30 Python
Python中输入和输出(打印)数据实例方法
Oct 13 Python
Pytorch使用MNIST数据集实现基础GAN和DCGAN详解
Jan 10 Python
JetBrains PyCharm(Community版本)的下载、安装和初步使用图文教程详解
Mar 19 Python
python属于哪种语言
Aug 16 Python
解决pycharm导入numpy包的和使用时报错:RuntimeError: The current Numpy installation (‘D:\\python3.6\\lib\\site-packa的问题
Dec 08 Python
Python3爬虫ChromeDriver的安装实例
Feb 06 Python
Python实现排序方法常见的四种
Jul 15 #Python
手把手教你使用TensorFlow2实现RNN
一篇文章弄懂Python关键字、标识符和变量
python开发飞机大战游戏
详解Python中下划线的5种含义
Python操作CSV格式文件的方法大全
openstack中的rpc远程调用的方法
You might like
php+mysql分页代码详解
2008/03/27 PHP
php htmlentities和htmlspecialchars 的区别
2008/08/18 PHP
php 邮件发送问题解决
2014/03/22 PHP
PHP中对各种加密算法、Hash算法的速度测试对比代码
2014/07/08 PHP
使用phpstorm和xdebug实现远程调试的方法
2015/12/29 PHP
php7 参数、整形及字符串处理机制修改实例分析
2020/05/25 PHP
js href的用法
2010/05/13 Javascript
TextArea不支持maxlength的解决办法(jquery)
2011/09/13 Javascript
jquery判断浏览器类型的代码
2012/11/05 Javascript
SeaJS入门教程系列之使用SeaJS(二)
2014/03/03 Javascript
js简单抽奖代码
2015/01/16 Javascript
jquery中添加属性和删除属性
2015/06/03 Javascript
js点击列表文字对应该行显示背景颜色的实现代码
2015/08/05 Javascript
jQuery实现的背景动态变化导航菜单效果
2015/08/24 Javascript
Jquery中使用show()与hide()方法动画显示和隐藏图片
2015/10/08 Javascript
JS使用eval解析JSON的注意事项分析
2015/11/14 Javascript
深入理解javascript中concat方法
2016/12/12 Javascript
利用jquery禁止外层滚动条的滚动
2017/01/05 Javascript
javascript表达式和运算符详解
2017/02/07 Javascript
vue.js的computed,filter,get,set的用法及区别详解
2018/03/08 Javascript
基于vue通用表单解决方案的思考与分析
2019/03/16 Javascript
微信小程序如何实现全局重新加载
2019/06/05 Javascript
[43:14]Liquid vs Optic 2018国际邀请赛淘汰赛BO3 第二场 8.21
2018/08/22 DOTA
python list 合并连接字符串的方法
2013/03/09 Python
Python urlopen()函数 示例分享
2014/06/12 Python
Python AES加密实例解析
2018/01/18 Python
关于python写入文件自动换行的问题
2018/06/23 Python
浅谈keras中的目标函数和优化函数MSE用法
2020/06/10 Python
Pycharm2020.1安装中文语言插件的详细教程(不需要汉化)
2020/08/07 Python
如何使用amaze ui的分页样式封装一个通用的JS分页控件
2020/08/21 HTML / CSS
倩碧英国官网:Clinique英国
2018/08/10 全球购物
100%植物性、有机、即食餐:Sakara Life
2018/10/25 全球购物
资产经营总监岗位职责
2013/12/04 职场文书
小学教师个人总结
2015/02/05 职场文书
亚布力滑雪场导游词
2015/02/09 职场文书
Python Pandas pandas.read_sql_query函数实例用法分析
2021/06/21 Python