Django4.2 学习 - 路由 Router(二)

1. 路由 router

在实际开发过程中,一个 Django 项目会包含很多的 app,这时如果我们只在主路由里进行配置就会显的杂乱无章,所以通常会在每个 app 里,创建各自的 urls.py 路由模块,然后从根路由出发,奖 app 所属的 url 请求,全部转发到相应的 urls.py 模块中,而这个从主路由转发到各个应用路由的过程叫做路由分发。

(1)路由匹配

1️⃣主路由直接匹配

在主 urls.py 中,直接写

1
2
3
4
5
6
7
8
9
10
11
from django.contrib import admin
from django.urls import path, include
from App.views import index

urlpatterns = [
path('admin/', admin.site.urls),

# 使用主路由
path('index/', index),

]

2️⃣子路由匹配

1
2
3
4
5
6
7
8
9
10
11
12
from django.contrib import admin
from django.urls import path, include
from App.views import index

urlpatterns = [

# 使用子路由
path('app/', include('App.urls')),

path('app2/', include('App2.urls')),

]
1
<a href="/user/userlist/">用户列表</a>

(2)使用子路由 + 命名空间

在实际应用中,Django 中可能存在多个应用程序,每个应用程序都有可能有自己的路由模块。为了防止路由冲突,Django 提供了命名空间(namespace)的概念。命名空间是一种奖路由命名为层次结构的方式,使得在查询路由时可以限定在命名空间内。(如果使用了命名空间的这种写法,则在之后的视图函数 / 模板中都要使用命名空间的写法,否则会报错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib import admin
from django.urls import path, include
from App.views import index

urlpatterns = [
path('admin/', admin.site.urls),

# 使用子路由+命名空间,include('子路由', 'appname'),namespace 一般和 appname 相同
path('user/', include(('App2.urls', 'App2'), namespace='App2')),

# 使用子路由+命名空间,
path('user/', include(('App.urls', 'App'), namespace='App')),
]

(3)反向解析

在 Django 框架中,正向解析是指根据 URL 配置文件中的 URL 模式来匹配用户请求的 URL,并调用相应的视图函数。这是 Django 处理 HTTP 请求的标准流程。
当用户在浏览器中输入一个 URL 并发送请求时,Django 会按照以下步骤进行正向解析:
1️⃣Django 接收到请求,并从请求中提取出 URL 路径。
2️⃣Django 遍历 URL 配置文件中的 URL 模式列表,尝试找到与请求 URL 匹配的模式。
3️⃣如果找到匹配的模式,Django 会调用与该模式关联的视图函数,并将请求传递给该视图函数进行处理。
4️⃣视图函数处理请求,并返回一个 HTTP 响应给用户。

反向解析是 Django 框架中的一个特性,它允许你在代码中使用视图函数或 URL 模式的名称来动态生成 URL,而不是直接硬编码 URL 字符串。这样做的好处是,当你需要修改 URL 模式时,只需要在 URL 配置文件中进行修改,而不需要在整个项目中查找和替换所有硬编码的 URL 字符串。
反向解析的流程如下:

1️⃣定义 URL 模式

在 Django 的 URL 配置文件中,你需要为每个视图函数或 URL 模式定义一个名称。例如:

1
2
3
4
5
6
from django.urls import path
from . import views

urlpatterns = [
path('userlist/', views.userlist, name='userlist'),
]

2️⃣在模板中使用反向解析

  • 在模板中,你可以使用 {% url %} 模板标签来生成 URL。例如:
1
<a href="{% url 'userlist' %}">用户列表</a>

这里的 userlist 就是之前定义的 URL 名称。

  • 带命名空间的反向解析

主路由 urls.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.contrib import admin
from django.urls import path, include
from App.views import index

urlpatterns = [
path('admin/', admin.site.urls),

# 使用主路由
path('index/', index),

# 使用子路由+命名空间
path('app2/', include(('App2.urls', 'App2'), namespace='App2')),

# 使用子路由+命名空间
path('app/', include(('App.urls', 'App'), namespace='App')),
]

templates/index.html

1
2
3
4
5
6
<h2>带命名空间的反向解析</h2>
{# App:userlist 为 namespace : name 的值,也就是 命名空间:}
<a href="{% url 'App:userlist' %}">用户列表</a>
<hr>
<a href="{% url 'App2:userlist2' %}">用户列表2</a>

3️⃣在视图中的反向解析 (带命名空间)

1
2
3
4
#这里 App 为命名空间,userlist 为 urls.py 中 path 的 name
def user_reverse(request):
url = reverse('App:userlist')
return redirect(url)

4️⃣在其他地方的反向解析

除了模板和视图,你还可以在其他地方使用反向解析,比如在表单的 action 属性中,或者在 JavaScript 代码中。

后续学习了,在来这里总结。

总结来说,反向解析的流程是:先在 URL 配置文件中为 URL 模式定义名称,然后在模板或视图中使用这些名称来动态生成 URL。这样做可以提高代码的可维护性和灵活性。

(4)路由传参

1️⃣单一参数:

1
2
3
4
5
6
7
# 使用 url 给视图函数传参数
path('index/', index)
path('detail/<int:id>/', detail)

# 给 url 取别名,那么在使用此 url 的地方可以使用别名。比如:
path('index/', index, name='index')
path('detail/<int:id>', detail, name='detail')

views.py

1
2
3
4
def user_detail(request, pk):
print(pk)
user = UserModel.objects.get(pk=pk)
return render(request, 'user_detail.html', {'user': user})

2️⃣多参数:

1
2
3
4
5
 
path('userab/<int:a>/<int:b>', user_ab, name='userab'),

# 正则写法
re_path(r'^userab2/(?P<a>\d+)/(?P<b>\d+)$', user_ab, name='userab2'),

views.py

1
2
def user_ab(request, a, b):
return HttpResponse('a={}, b={}'.format(a, b))

(5)重定向

在 Django 中,重定向是一种常见的操作,通常用于在处理完某个请求后,将用户引导到另一个页面。Django 提供了多种方式来实现重定向,以下是一些常见的方法:

1️⃣使用 redirect 函数:

views.py

1
2
3
# 使用 redirect 函数
def user_redirect(request):
return redirect('/app/userlist/')
  • redirect 函数接受一个 URL 作为参数,并返回一个 HTTP 重定向响应。
  • 你可以传递一个完整的 URL,或者一个视图函数的名称,Django 会自动解析并生成正确的重定向 URL。

2️⃣使用 reverse 函数生成 URL

reverse 函数用于根据视图函数的名称生成 URL, 这在你需要动态生成重定向 URL 时非常有用。

App/urls.py

1
2
3
4
5
# 重定向使用

path('userlist/', user_list, name='userlist'),

path('userreverse/', user_reverse, name='userreverse'),

views.py

1
2
3
4
# 如果之前使用了命名空间的写法,这里的 reverse 也必须用命名空间的写法
def user_reverse(request):
url = reverse('App:userlist')
return redirect(url)

3️⃣“位置参数”

views.py

1
2
3
4
# 使用重定向+位置参数
def user_reverse2(request):
url = reverse('App:userdetail', args=(1,))
return redirect(url)

4️⃣“关键字参数”

views.py

1
2
3
4
# 使用重定向+ 关键字参数
def user_reverse3(request):
url = reverse('App:userdetail', kwargs={'pk': 1})
return redirect(url)

2. 关键代码

(1)主路由 urls.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.contrib import admin
from django.urls import path, include
from App.views import index

urlpatterns = [
path('admin/', admin.site.urls),

# 使用主路由
path('index/', index),

# 使用子路由
# path('app/', include('App.urls')),
# path('user/', include(('App.urls', 'App'), namespace='App')),
# 使用子路由
# path('app2/', include('App2.urls')),

# 使用子路由+命名空间
path('app2/', include(('App2.urls', 'App2'), namespace='App2')),

# 使用子路由+命名空间
path('app/', include(('App.urls', 'App'), namespace='App')),
]

(2)子路由 1App/urls.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from django.contrib.auth.views import LoginView
from django.urls import path, re_path
from App.views import *

urlpatterns = [
path('userlist/', user_list, name='userlist'),

path('userdetail/<int:pk>/', user_detail, name='userdetail'),

path('userab/<int:a>/<int:b>', user_ab, name='userab'),

# 正则写法
re_path(r'^userab2/(?P<a>\d+)/(?P<b>\d+)$', user_ab, name='userab2'),

# 重定向使用
path('userredirect/', user_redirect, name='userredirect'),

# 重定向使用
path('userreverse/', user_reverse, name='userreverse'),

# 重定向使用
path('userreverse2/', user_reverse2, name='userreverse2'),
# 重定向使用
path('userreverse3/', user_reverse3, name='userreverse3'),
]

(3)子路由 2App2/urls.py

1
2
3
4
5
6
from django.urls import path
from App2.views import *

urlpatterns = [
path('userlist/', user_list, name='userlist2'),
]

(4)App/views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from django.http import HttpResponse
from django.shortcuts import render, redirect, reverse

from App.models import *


# Create your views here.
def index(request):
return render(request, 'index.html')


def user_list(request):
users = UserModel.objects.all()
return render(request, 'user_list.html', {'users': users})


def user_detail(request, pk):
print(pk)
user = UserModel.objects.get(pk=pk)
return render(request, 'user_detail.html', {'user': user})


def user_ab(request, a, b):
return HttpResponse('a={}, b={}'.format(a, b))


# 使用 redirect 的重定向
def user_redirect(request):
return redirect('/app/userlist/')


# 使用 reverser
def user_reverse(request):
url = reverse('App:userlist')
return redirect(url)


# 使用重定向+位置参数
def user_reverse2(request):
url = reverse('App:userdetail', args=(1,))
return redirect(url)


# 使用重定向+ 关键字参数
def user_reverse3(request):
url = reverse('App:userdetail', kwargs={'pk': 1})
return redirect(url)

(5)App2/views.py

1
2
3
4
5
6
from django.shortcuts import render

# Create your views here.

def user_list(request):
return render(request, 'user_list2.html')

(6)Templates/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
{#测试注释#}
<h2>1.正常的路由跳转</h2>
<hr>
{#这里是直接访问了视图函数,通过视图函数做相应的跳转#}
<a href="/app/userlist/">用户列表</a>

<hr>

<h2>反向解析</h2>
{# userlist 为 path路由的 name 值#}
{#<a href="{% url 'userlist' %}">用户列表</a>#}
<hr>

<h2>带命名空间的反向解析</h2>
{# App:userlist 为 namespace:name 值#}
<a href="{% url 'App:userlist' %}">用户列表</a>
<hr>
<a href="{% url 'App2:userlist2' %}">用户列表2</a>

<h2>重定向redirect</h2>
{# 重定向到用户列表页面#}
<a href="{% url 'App:userredirect' %}">重定向</a>
<hr>
<h2>重定向reverse+redirect</h2>
<a href="{% url 'App:userreverse' %}">重定向2</a>
<hr>
<h2>重定向带+“位置参数”</h2>
<a href="{% url 'App:userreverse2' %}">重定向带位置参数</a>
<hr>
<h2>重定向带+“关键字参数”</h2>
<a href="{% url 'App:userreverse3' %}">重定向带关键字参数</a>

</body>
</html>

(7)Templates/user_list.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
</head>
<body>
<h2>用户列表</h2>
<hr>

<ul>
{% for user in users %}
<li>
<a href="{% url 'App:userdetail' user.id %}">{{ user.username }}</a>
</li>
{% endfor %}
</ul>


</body>
</html>

(8)Templates/user_list2

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户列表 2</title>
</head>
<body>
<h2>用户列表 2</h2>
</body>
</html>

(9)Templates/user_detail.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户详情</title>
</head>
<body>

<ul>
<li>
{{ user.username }} -- {{ user.age }}
</li>
</ul>

</body>
</html>

(10)App/models.py

1
2
3
4
5
6
7
from django.db import models

# Create your models here.
class UserModel(models.Model):
username = models.CharField(max_length=100)
age = models.PositiveIntegerField(default=18) # 非负数
# 0: normal user, 1: admin

3. 相关参考

https://www.bilibili.com/video/BV1W34y1c7Rn?spm_id_from=333.788.videopod.episodes&vd_source=fd555860cd7cf9b09d9279e5deaabb9d&p=21