二、django中的路由系统

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

django中的路由系统

django中路由的作用和路由器类似当一个用户请求Django站点的一个页面时是路由系统通过对url的路径部分进行匹配一旦匹配成功就导入并执行对应的视图来返回响应。

django如何处理请求

  1. 当一个请求来到时django首先到项目中查找根URLconf模块在其中查找路由匹配规则。
  2. 根URLconf模块就是项目文件目录下的urls.py文件。在这个文件中定义了一个变量urlpatterns。它是一个列表其中每一个元素都是一个url模式定义了url和视图函数的对应关系。
  3. django按顺序运行每个url模式并在与请求的url匹配的第一个模式停止。
  4. 一旦去中一个url模式匹配django将导入并调用该视图。
  5. 如果没有匹配的模式或者在此过程中任何时候引发异常django调用错误处理视图。

路由模块

在django中路由模块一般命名为url.py

每一个路由模块中都会包含一个urlpatterns变量它是一个django.urls.path()或者django.urls.re_path()实例的列表。

根路由模块

最外层的路由模块路由解析的入口。

django通过设置ROOT_URLCONF来确定主路由模块通常是项目目录下的urls.py模块。

子路由

主路由包含的其他路由都是子路由。

一般都是各自应用目录下的urls.py模块。

path()

  • path(route,view, kwargs=None,name=None)

  • path函数返回一个对象表示一个路由规则

  • route一个字符串表示url规则

  • view: 一个视图
  • kwargs:一个字典需要传入的额外参数
  • name:url的命名

案例

上一节我们在crm应用中定义了如下路由

path('index/', views.index)

'index/'和视图views.index进行映射。

include()

  • include(module, namespace=None)

  • 将一个子路由导入到一个URLconf模块中

  • module: URLconf模块(或模块名称)

案例

上一节我们在根路由中通过include包含了crm子路由

path('crm/', include('crm.urls'))

crm/和子路由crm.urls进行映射。

URLconf在什么上查找

请求的url会被看做是一个普通的Python字符串URLconf在其上查找并匹配。

进行匹配时不包含GETPOST请求方式的参数以及域名。

例如https://www.example.com/myapp/ 请求中URLconf 将查找 myapp/

在 https://www.example.com/myapp/?page=3 请求中URLconf 仍将查找 myapp/ 。

URLconf不检查使用哪种请求方法。

换句话说所有请求方法对同一个URL无论是POST请求GET请求等等都将路由到相同的视图函数。

案例

所以在浏览器中访问地址http://127.0.0.1/crm/index/时URLconf将查找crm/index/

第一步在跟路由中进行匹配可以匹配到

path('crm/', include('crm.urls'))

第二步当遇到include时它会将匹配到该点的URL部分crm/切掉并将剩余字符串index/发送到包含的crm.urls模块中进一步匹配

第三/indexcrm.urls路由中可以匹配到

path('index/', views.index)

没有下一步匹配直接执行对应的视图函数views.index

注意这个过程会递归的进行中间遇到任何一条匹配的路由就会返回。

  

 

在URL中捕获参数

django允许在url中捕获值若要从URL中捕获值请使用尖括号。

尖括号定义变量名捕获的值传递给视图函数相同名称的参数。格式如下

'<参数名>'

案例

写一个学生详情接口通过crm/students/n/返回id为n的学生的信息。

视图代码

# crm/views.py
def detail(request, pk):
    return HttpResponse('学生id为{}的详情'.format(pk))        # 模拟返回对应学生的详情

路由代码

# crm
urlpatterns = [
    ...
    path('students/<pk>/', views.detail)
]

现在在浏览器中访问http://127.0.0.1:8000/crm/students/2/会返回页面

注意看路由部分的<pk>这里的pk对应视图函数的pk形参。django会自动匹配url中这部分的字符串2然后传递给detail函数的pk形参。

路径转换器

上面的案例有个漏洞如果我们在浏览器中输入http://127.0.0.1:8000/crm/students/aaa/我们发现它依然可以访问。

在实际开发中这显然不对id不可能是个字符串。当然可以在view中进行类型转换但是不够通用。django中设计了路径转换器能够在路由匹配时自动进行转换。

以下路径转换器在默认情况下是可用的

  • str: 匹配除了路径分隔符/之外的任何非空字符串。如果表达式中不包含转换器默认为字符串转换器。

  • int: 匹配0或任何整数。返回一个整数类型

  • slug: 匹配任何由ASCII字符或数字组成的slug字符串加上连字符和下划线。
  • uuid: 匹配格式化的UUID。为了防止多个url映射到同一个页面必须包含破折号并且必须是小写字母。例如075194d3-6885-417e-a8a8-6c931e272f00
  • path: 匹配任何非空字符串包括路径分隔符/。这允许匹配完整的URL路径而不是像str那样仅匹配url路径部分。

路径转换器的使用方式非常简单只需要在捕获符号<>中以以下语法即可

'<转换器:参数名>'

我们可以将上面的案例修改为

path('students/<int:pk>/', views.detail)

然后我们再次访问`http://127.0.0.1:8000/crm/students/aaa/,结果是404。

当然我们也可以捕获多个值看如下案例

视图代码

# crm/views.py
def student_list(request, year, month):
    return HttpResponse('{}年{}月创建的学生列表'.format(year, month))

路由代码

# crm/urls.py
urlpatterns = [
    ...
    path('students/<int:year>-<int:month>/', views.student_list),
    path('students/<int:year>/<int:month>/', views.student_list)
]

那么通过urlhttp://127.0.0.1:8000/crm/students/2022/01http://127.0.0.1:8000/crm/students/2022-01/会得到相同的结果。

但是如果访问http://127.0.0.1:8000/crm/students/9527-100/也会得到结果

这显然又是bug。

路径转换器只能进行简单的类型转换和匹配还需要更强大的匹配功能需要用到re_path()函数。

re_path()

  • re_path(route,view, kwargs=None,name=None)函数返回一个对象表示一条路由规则。
  • route: 一个字符串表示一个url规则
  • view一个视图
  • kwargs: 一个字典需要传入的额外参数
  • name: url命名

path()不同的是route部分包含正则表达式。

当进行匹配时从正则表达式中捕获的组会被传递到视图中。

如果组是命名的则作为命名参数否则作为位置参数。值以字符串的形式专递不进行任何类型转换。

命名正则表达式分组的语法是(?P<name>pattern)其中name是组的名称pattern是要匹配的某个模式。

下面是前面例子中的路由使用正则表达式重写

re_path(r'^students/(?P<year>\d{4})-(?P<month>[1-9]|1[0-2])/$', views.student_list),
re_path(r'^students/(?P<year>\d{4})/(?P<month>[1-9]|1[0-2])/$', views.student_list)

这样写和之前的路由匹配一致只是

  • 匹配的url会受到限制例如100月将不再匹配因为月份整数被限制为1-12。
  • 捕获的每个参数都以字符串的形式发送到视图。
  • 当从使用path()切换到re_path()或相反时特别重要的是注意视图参数的类型会发生变化因此可能需要调整视图。

使用没有命名分组的正则表达式

除了命名组语法例如(?P<year>\d{4})还可以使用较短的未命名组例如(\d{4})。这种写法并不特别推荐因为它更容易在匹配的预期含义和视图参数之间意外的一如错误。

注意在实际使用中建议只使用一种要么命名要么不命名因为当两种方式混合使用时会忽略未命名的组只将命名的组传递给视图函数。

url命名

path() re_path()还有一个参数那就是name这个参数可以给我们的url命一个名。

那它有什么作用呢

在实际开发中经常需要获取最终形式的url比如嵌入的页面链接和服务端导航(重定向)。

我们来模拟一个登录过程创建一个登录的函数视图如下

# crm/views.py
from django.shortcuts import redirect
...


def login(request):
    return redirect('/crm/index/')

配置好url

# crm/urls.py
path('login/', views.login)

当我们访问/crm/login/时会发现页面被重定向到了/crm/index/但是这里有一个问题重定向这里的url是硬编码的万一将来我们要修改这个url(这个几率很大)那么我们需要在代码中修改所有硬编码的url部分。这显然不利于维护。所以强烈建议不要硬编码URL(这是一个费力不能扩展容易出错的注意)。

django提供一个django.shortcuts.reverse()函数它接受一个url的命名能反向解析出url的绝对路径。

给crm应用的每条路由都添加一个name。

urlpatterns = [
    path('index/', views.index, name='index'),
    path('students/<int:pk>/', views.detail, name='student_detail'),
    path('students/<int:year>/<int:month>/', views.student_list, name='student_list'),
    path('login/', views.login, name='login')
]

然后修改登录视图如下

from django.shortcuts import reverse
def login(request):
    url = reverse('index')
    return redirect(url)

这样不管怎么修改urlreverse都可以动态的解析出url。

当有url参数时可以通过args,kwargs进行传递例如

reverse('student_list', kwargs={'year': 2021, 'month': 12})
# 或
reverse('student_list', args=(2021,12))
# 都可以解析出 /crm/students/2021/12/

注意在reverseargs,kwargs两个参数不能同时使用。

app_name

将url命名为indexlogin非常常见一个项目中不同的app给url相同的命名那怎么区分不同应用相同名称的url呢

非常简单在应用下的urls.py文件中定义一个app_name变量给它赋值为引用的名称例如在crm/urls.py中定义app_name变量如下

# crm/urls.py
...
app_name = 'crm'            # 一般和引用同名
...

定义app_name之后再解析url时需同时传入app_name格式如下

'app_name:url_name'

那么要解析index的url的代码如下

reverse('crm:index')
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go