Django路由详解
一、路由route
在实际开发过程中,一个Django项目会包含很多的app,这时候如果我们只在主路由里进行配置就会显得杂乱无章,所以通常会在每一个app里,创建各自的urls.py模块,然后从跟路由出发,将app所属的url请求,全部转发到相应的urls.py模块中。而这个从主路由转发到各个应用路由的过程叫做路由的分发。
1.1 路由匹配:
- 使用url给视图函数传参数
path('index/',index)
path('detail/<int:id>/',detail)
- 给url取别名,那么在使用此url的地方可以使用别名。比如:
path('index/',index,name='index')
path('detail/<int:id>/',detail,name='detail')
1.2 命名空间
在实际应用中,Django中可能存在多个应用程序,每个应用程序都可能有自己的路由模块,即每一个应用都可以用一个命名空间取定义。为了防止路由冲突,Django提供了命名空间(namespace)的概念。命名空间是一种将路由命名为层次结构的方式,使得在查询路由时可以限定在该命名空间内。
在跟路由中可以设置命名空间:
path('app/',include(('App.urls','App'),namespace='App'))
1.3 反向解析
Django路由反向解析是一个非常重要的功能,它可以让我们在代码中使用路由别名代替URL路径,在修改URL时避免代码中的硬编码依赖,同时也可以提高可读性和可维护性。
- 在视图函数中,反向解析url:
from django.shortcuts import render,redirect,reverse
def buy(request):
return redirect(reverse('index'))
return redirect(reverse('detail',args=[2]))
return redirect(reverse('detail',kwargs={'id':2}))
- 在templates中使用别名
{% url 'detail' stu.id %}
- 使用命名空间
指定命名空间后,使用反向解析时需要加上命名空间,比如:
1.在视图函数中:
return redirect(reverse('App:index'))
2.在templates中:
{% url 'App:index' %}
二、创建路由
2.1 直接使用跟路由
首先要从views.py中导入视图函数,然后写路由user/,映射的视图函数是user()函数
2.2 创建子路由(include)
每一个应用只能对应一个子路由,如果要创建子路由,则在应用下创建一个新的python文件,命名为urls.py。
要导入include()方法:
from django.urls import path,include
然后在项目目录下的urls.py即跟路由中为应用创建子路由:
接着我们的子路由就不用再跟路由中定义了,需要在应用的urls.py中定义,如果要访问子路由,首先到跟路由中找到App01,然后再到APP01下的urls.py中查找对应的子路由。
在应用的urls.py中,我们需要定义子路由,就和跟路由中定义跟路由一样。
2.3 创建子路由(include和命名空间namespace)
path()中第一个参数是应用的路径,include()中的第一个参数是应用的子路由的路径,第二个参数是命名空间namespace,namespace的值最好和应用的名字一致。
2.4 在应用中创建子路由
在应用中新建一个urls.py,它用于存放该应用的子路由。注意,一个应用只能对应一个子路由文件,即应用目录下只能包含一个urls.py。
子路由的结构与跟路由一样:
如果使用命名空间,则path中需要有3个参数,即比不用namespace需要多写一个参数namespace。
编写视图函数user():
编写模板文件user.html:
路由、视图函数、模板文件都准备好之后,就可以访问。在终端输入命令python manage.py runserver
,打开网址127.0.0.1:8000/App01/user
三、页面跳转——使用反向解析和命名空间
使用前端可以通过超链接实现页面跳转。在后端可以通过Django实现页面跳转,即所有的跳转请求都需要经过后端。
首页index已经创建好了,接下来创建一个用户列表userlist页面:
- 视图函数:
def user_list(request):
return render(request,'user_list.html')
- 路由:
(使用include)
# 跟路由:
path('App01/',include('App01.urls')),
# 子路由:
path('userlist/',user_list,name='userlist'),
或者(使用include和namespace)
# 跟路由:
path('App01/',include(('App01.urls','App01'),namespace='App01')),
# 子路由:
path('userlist/',user_list,name='userlist'),
注意:这两种使用子路由的方法一次只能使用其中一种。
- 模板
接下来要实现两个页面的跳转:
(1)通过url路径来实现跳转(超链接)
在index.html中使用超链接跳转到用户列表页面。使用超链接,通过url路由路径实现页面跳转。
刷新之后的首页页面:
点击之后,即可跳转到用户列表页面。因为我们点击超链接,对页面发起请求,然后就会查找路由进行匹配,匹配到正确的路由之后访问对应的视图函数user_list,视图函数将网页请求和模板渲染并返回用户列表页面,就实现了从首页跳转到用户列表页面。
(2)反向解析
正常来说,我们访问后台需要在浏览器中有路由地址,有路由地址之后,先匹配路由,再访问视图函数,再进入到模板文件。反向解析就是先到html中找url的name,通过name来匹配url,即通过name找到对应url的路由路径和对应的视图函数,再进入到视图函数中。
对应的路由描述方式(使用include):
页面效果:
(3)带命名空间的反向解析
使用命名空间的好处是,如果在不同应用中有相同的name,那么就可以通过命名空间区分它们,因为一个应用只能对应一个命名空间,一般命名空间的名字与所在应用的名字相同。
注意:方法2和方法3只能用一个,否则会报错。
对应的路由描述方式(使用include和命名空间namespace):
页面效果:
四、路由传参
通过路由路径传参
我们获取用户列表中的用户id,通过url传参的方式访问用户详情页面。
用户详情的视图函数:
# 用户详情
# 通过id来区分不同用户
def user_detail(request,uid):
print('uid:',uid)
user=UserModel.objects.get(pk=uid) # pk:primary key 主键
return render(request,'user_detail.html',{'user':user})
用户详情的url(子路由):
通过url路径传参
# 用户详情
# int表示参数的类型为整数
# 通过url传参,url路径中参数的名字需要与视图函数中参数的名字对应
path('userdetail/<int:uid>/',user_detail,name='userdetail'),
用户详情页的html文档(userdetail):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户详情页</title>
</head>
<body>
<h2>用户详情</h2>
</body>
</html>
通过url来访问用户详情页的页面:
userdetail后的数字表示用户列表中自动生成的id。
接下来尝试从用户列表页面跳转到用户详情页面:
在用户列表的html文档中使用a标签写上超链接,这里使用带命名空间的反向解析方式进行页面跳转,要通过url传递的参数放在末尾。
接下来访问用户列表页面:
通过点击超链接就可以实现页面跳转,点击不同的用户,对应的url的参数不同,因为不同用户的id不同。
点击第一个用户,跳转页面的url是:http://127.0.0.1:8000/App01/userdetail/1/
点击第二个用户,跳转页面的url是:http://127.0.0.1:8000/App01/userdetail/2/
点击第三个用户,跳转页面的url是:http://127.0.0.1:8000/App01/userdetail/3/
……
在用户详情页展示用户的信息,因为通过id可以跳转到不同用户页面,所以要在id对应的用户页面展示该用户的信息。
我们通过视图函数user_detail将用户信息传递给模板user_detail.html,在userdetail页面将用户的姓名和年龄显示出来。
五、路由传递多个参数
- 视图函数
# 路由传递多个参数
def user_ab_view(request,a,b):
return HttpResponse(f'a:{a} - b:{b}')
- 路由
# 路由传递多个参数
path('userab/<int:a>/<int:b>/',user_ab_view,name='userab'),
注意,在使用的时候,视图函数中的参数名要和路由中的参数名保持一致,名字对应赋值。
访问userab页面,后面必须要跟上两个参数a和b,a和b都是整数。我们通过HttpResponse返回的是a和b的值。
六、重定向Redirect
需要在视图函数中导入redirect和reverse。
reverse(name)获取到的是该name对应的路由的路径,如,reverse(‘index’)获取到的是’index/',args表示位置传参,kwargs表示的是关键字传参。
使用redirect进行的是页面跳转,这种跳转在后台是在视图函数之间进行跳转。
在视图函数中进行页面跳转——重定向:
(1) 访问外部页面
- 视图函数
# 在视图函数中进行页面跳转——重定向
# 在内部进行自动跳转,而不是点击跳转
def my_redirect(request):
return redirect('https://www.sogou.com')
- 路由
# 重定向
# 从mydirect页面跳转到别的页面,但是不会从别的页面跳转过来
path('myredirect/',my_redirect),
访问myredirect页面会自动跳转https://www.sogou.com
(2)访问本地路径
def my_redirect(request):
# return redirect('https://www.sogou.com')
return redirect('/App01/userlist/')
访问mydirect会自动跳转userlist页面。
(3)使用反向解析(带命名空间)——位置参数传参
注意:使用带命名空间的反向解析时,跟路由中也对应的要使用命名空间。
跟路由:
# 3.使用子路由(include和命名空间namespace)
# 使用include和命名空间namespace
path('App01/',include(('App01.urls','App01'),namespace='App01')),
位置参数传参需要使用reverse中的args参数,以元组的形式传参。
使用带命名空间的反向解析,进行重定向时,reverse中需要写namespace:name,namespace即应用对应的命名空间,name即要跳转的路由的name。
视图函数:
# 在视图函数中进行页面跳转——重定向
# 在内部进行自动跳转,而不是点击跳转
def my_redirect(request):
# 访问外部页面
# return redirect('https://www.sogou.com')
# 访问本地路径
# return redirect('/App01/userlist/')
# 反向解析
# 跟路由中用到了namespace,在重定向中使用反向解析时需要和跟路由中的写法保持一致
# 如果使用的是带命名空间的反向解析,那么在进行重定向时就要写命名空间
# 如果使用的是不带命名空间的反向解析,那么在重定向时就不需要带命名空间
# 如果要跳转的路由中有参数,那么在重定向时需要写上arg参数(元组形式)
# reverse('App01:userdetail',args=(1,)) ——> userdetail/1/
return redirect(reverse('App01:userdetail',args=(1,)))
在反向解析的重定向中,获取到要跳转的路由的name之后,进行路由匹配,找到name对应的path,获取路径为"userdetail/int:uid/",通过args传递的参数同时传递给路由url中的参数。如:reverse(‘App01:userdetail’,args=(1,)) ——> userdetail/1/
路由:
# 用户详情
# int表示参数的类型为整数
# 通过url传参,url路径中参数的名字需要与视图函数中参数的名字对应
path('userdetail/<int:uid>/',user_detail,name='userdetail'),
访问myredirect页面就会自动跳转到对应的用户详情页。
(4)使用反向解析(带命名空间)——关键字参数传参
关键字参数传参需要使用reverse中的kwargs参数,以字典形式传参。
关键字参数传参的字典中的键对应要跳转的路由中的参数,名字要一致。字典形式也可以给多个参数传参,写成多对键值对形式。
# 在视图函数中进行页面跳转——重定向
# 在内部进行自动跳转,而不是点击跳转
def my_redirect(request):
# 访问外部页面
# return redirect('https://www.sogou.com')
# 访问本地路径
# return redirect('/App01/userlist/')
# 反向解析
# 跟路由中用到了namespace,在重定向中使用反向解析时需要和跟路由中的写法保持一致
# 如果使用的是带命名空间的反向解析,那么在进行重定向时就要写命名空间
# 如果使用的是不带命名空间的反向解析,那么在重定向时就不需要带命名空间
# 如果要跳转的路由中有参数,那么在重定向时需要写上arg参数(元组形式)
# reverse('App01:userdetail',args=(1,)) ——> userdetail/1/
# return redirect(reverse('App01:userdetail',args=(1,)))
# 关键字参数传参
# 关键字参数传参需要使用reverse中的kwargs参数,以字典形式传参。
return redirect(reverse('App01:userdetail',kwargs={'uid':2}))
访问myredirect页面会自动跳转http://127.0.0.1:8000/App01/userdetail/2/ 。
(5)反向解析(不带命名空间)
注意:使用不带命名空间的反向解析时,跟路由中也对应的不可以使用命名空间。
跟路由:
# 2.使用子路由,使用include
# 一个应用对应一个子路由
path('App01/',include('App01.urls')),
视图函数:
# 在视图函数中进行页面跳转——重定向
# 在内部进行自动跳转,而不是点击跳转
def my_redirect(request):
# 1.访问外部页面
# return redirect('https://www.sogou.com')
# 2.访问本地路径
# return redirect('/App01/userlist/')
# 3.反向解析(带命名空间)
# 跟路由中用到了namespace,在重定向中使用反向解析时需要和跟路由中的写法保持一致
# 如果使用的是带命名空间的反向解析,那么在进行重定向时就要写命名空间
# 如果使用的是不带命名空间的反向解析,那么在重定向时就不需要带命名空间
# 如果要跳转的路由中有参数,那么在重定向时需要写上arg参数(元组形式)
# reverse('App01:userdetail',args=(1,)) ——> userdetail/1/
# return redirect(reverse('App01:userdetail',args=(1,)))
# 关键字参数传参
# 关键字参数传参需要使用reverse中的kwargs参数,以字典形式传参。
# return redirect(reverse('App01:userdetail',kwargs={'uid':2}))
# 4.反向解析(不带命名空间)
return redirect(reverse('userdetail',args=(1,)))
注意:如果使用了命名空间,那么后面的反向解析(包括视图函数和模板中)都要使用命名空间,否则会报错。