路由就是一种映射关系,将客户端的url请求和应用程序的视图进行绑定

在django中所有的路由最终都被保存到一个变量 urlpatterns., urlpatterns必须声明在主应用下的urls.py总路由中。这是由配置文件settings设置的。

1.路由

1.1 传统路由

from django.contrib import admin
from django.urls import path
from apps.web import views

urlpatterns = [
    path('home/', views.home),\
    path('news/<int:nid>/edit/', views.news),  # 可以传参
]
from django.shortcuts import render, HttpResponse


def home(request):
    return HttpResponse("成功")


def news(request, nid):
    print(nid)
    page = request.GET.get("page")
    return HttpResponse("新闻")
  • int,整数

  • str,字符串 /

  • slug,字母+数字+下滑线+-

  • uuid,uuid格式

  • path,路径,可以包含 /

1.2 正则匹配路由

正则路由匹配 结合了正则的知识来进行匹配

re_path(r'^articles/([0-9]{4})/$', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive2),

 

2.路由分发

在多app中,我们一般不会将所有的路由放在主项目的下的urls.py文件中,会将每个子app下的路由放在自身下的urls.py中,这需要通过主路由的分发

2.1 常规分发

 

 

 主路由

from django.contrib import admin
from django.urls import path, include

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

子路由

from django.urls import path,include
from app01 import views

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

 

2.2 路由分发的本质

通过查看include函数的源码,我们会发现,include返回的是一个含有3个元素的元组

 1 def include(arg, namespace=None):
 2     app_name = None
 3     if isinstance(arg, tuple):
 4         # Callable returning a namespace hint.
 5         try:
 6             urlconf_module, app_name = arg
 7         except ValueError:
 8             if namespace:
 9                 raise ImproperlyConfigured(
10                     'Cannot override the namespace for a dynamic module that '
11                     'provides a namespace.'
12                 )
13             raise ImproperlyConfigured(
14                 'Passing a %d-tuple to include() is not supported. Pass a '
15                 '2-tuple containing the list of patterns and app_name, and '
16                 'provide the namespace argument to include() instead.' % len(arg)
17             )
18     else:
19         # No namespace hint - use manually provided namespace.
20         urlconf_module = arg
21 
22     if isinstance(urlconf_module, str):
23         urlconf_module = import_module(urlconf_module)
24     patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
25     app_name = getattr(urlconf_module, 'app_name', app_name)
26     if namespace and not app_name:
27         raise ImproperlyConfigured(
28             'Specifying a namespace in include() without providing an app_name '
29             'is not supported. Set the app_name attribute in the included '
30             'module, or pass a 2-tuple containing the list of patterns and '
31             'app_name instead.',
32         )
33     namespace = namespace or app_name
34     # Make sure the patterns can be iterated through (without this, some
35     # testcases will break).
36     if isinstance(patterns, (list, tuple)):
37         for url_pattern in patterns:
38             pattern = getattr(url_pattern, 'pattern', None)
39             if isinstance(pattern, LocalePrefixPattern):
40                 raise ImproperlyConfigured(
41                     'Using i18n_patterns in an included URLconf is not allowed.'
42                 )
43     return (urlconf_module, app_name, namespace)
View Code
# 1. 不使用include函数
path('user/', ([
                   path('add/', views.login),
                   path('delete/', views.login),   # /user/delete/
                   path('edit/', views.login),
                   path('list/', views.login),
               ], None, None)),

# 2. 使用include,但是参数为字符串,也就是指定子路由的文件路径
path('user/', include("apps.api.urls")),  # 一般是每个app中urls


# 3. 使用include,使用一个元组为参数
path('user/', include(("apps.api.urls", None))),  # 一般是每个app中urls

 

3.name/namespace

name就是给路由起一个别名,nameapce是为了辅助name的,防止在多app或者多人开发中name重名的问题

在实际开发中,一个url可能是非常长的,我们就可以利用name+ namespace的方式反向生成对应的URL

  • 重定向使用

  • 模板语法中使用

name的使用

urlpatterns = [
    path('login/', views.login, name="v1"),
    path('auth/', views.auth, name="v2"),
]

namespace的使用

urlpatterns = [
    path('api/', include("apps.api.urls",namespace='x1')),
    path('web/', include("apps.web.urls",namespace='x2')),
]

namespace在路由分发的过程是可以嵌套使用的

namespace1:namespace2:name

 

4.反向解析

反向解析就是根据路由的namespace和name生成对应的路由

主路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(('app01.urls', 'app01'), namespace='app01'))
]

注意:在指定namespace之后,需要指定app_name

子路由

from django.urls import path, include
from app01 import views

urlpatterns = [
    path('index/<int:nid>', views.index, name='index'),
]

 

视图中的reverse使用反向解析

def index(request, nid):
    now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(reverse("app01:index", kwargs={"nid": nid}))  # /app01/index/88
    return render(request, "index.html", {"now_time": now_time, "nid": nid})

模板语法中的url使用反向解析

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>{{ now_time }}</p>
    <p><a href="{% url "app01:index" nid %}">hello</a></p>
</body>
</html>

 

5.重定向

 

 

 当我们希望客户端主动请求某一个URL的时候,就可以发起重定向,比如用户登录成功,重定到主页页面

通过redirect实现

from django.shortcuts import render, HttpResponse, redirect
from datetime import datetime
from django.urls import reverse


# Create your views here.
def index(request, nid):
    now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(reverse("app01:index", kwargs={"nid": nid}))  # /app01/index/88
    # return redirect("https://www.baidu.com/")  # 完整url
    return redirect(reverse("app01:index", kwargs={"nid": nid}))  # 反向生成

 

6.补充

6.1  路由最后一个/

在配置文件中默认APPEND_SLASH = True

路由
path('login/', views.login),

请求
    http://127.0.0.1:8000/login/   成功

    http://127.0.0.1:8000/login    django,重定向301,发送的是GET
    http://127.0.0.1:8000/login/   成功
path('login', views.login),
http://127.0.0.1:8000/login 成功 http://127.0.0.1:8000/login/ 失败

在配置文件中设置APPEND_SLASH = False

path('login/', views.login),
    http://127.0.0.1:8000/login/   成功

    http://127.0.0.1:8000/login    失败
path('login', views.login),
    http://127.0.0.1:8000/login/   失败

    http://127.0.0.1:8000/login    成功

 

 

6.2 当前匹配对象

在视图函数中通过request参数resolve_match获取

    ResolverMatch(
        func=app01.views.index,      # 匹配的视图函数
        args=(),                     # 列表参数
        kwargs={'nid': 88},          # 字典参数
        url_name=index,              # url_name
        app_names=['app01'],         # app_name
        namespaces=['app01'],        # namespace
        route=app01/index/<int:nid>  # router
    )
from django.shortcuts import render, HttpResponse
from datetime import datetime
from django.urls import reverse


# Create your views here.
def index(request, nid):
    print(request.resolver_match)  # 获取当前匹配路由的对象
    """
    ResolverMatch(
        func=app01.views.index, 
        args=(), 
        kwargs={'nid': 88}, 
        url_name=index, 
        app_names=['app01'], 
        namespaces=['app01'], 
        route=app01/index/<int:nid>
    )
    """
    now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(reverse("app01:index", kwargs={"nid": nid}))  # /app01/index/88
    return render(request, "index.html", {"now_time": now_time, "nid": nid})

 

posted on 2022-11-16 13:58  阿明明  阅读(39)  评论(0编辑  收藏  举报