django之路由层

django之路由层

路由层,主要是路由地址与视图函数的映射关系,如果将网站比作一本书,那么路由就相当于这本书的目录。

路由匹配

在目前的主流版本2.x及以上版本中,我们默认采取path函数记录映射关系,

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home_page),
    path('article/<str:info>/', views.article_page),
    path('register/', views.register_page),
    path('login/', views.login_page),
]

path函数的第一个参数就是预设的各种路由,当我们在网页中输入网址,我们浏览器会将网址中路由解析出来依次与我们预设的路由比对,如果比对成功会去path()的第二个参数所对应的视图函数中(自动传入request对象作为第一个实参)执行并返回一个界面。

如果没有这么一个界面,那么django也提供了默认返回的界面如下:

img

这个网页会提示我们没有找到对应的网页,并且提示我们为什么找不到。

如这里就是没有提前配置我们胡乱输入的路由asef

django1.x版本默认使用的配置对应函数的方式是url("", views.func),其第一个参数是以正则表达式的方式匹配我们的路由的。

路由匹配小优化

在django的任何版本中,如果我们在输入路由时没有在后面跟/,那么在匹配不到路由时,会加上斜杠再请求匹配一遍。

验证:我们预设的路由中有'admin/',那么我们只输入路由中只输入admin,观察其网络请求的情况:

img

这个路由匹配优化可以在配置文件中设置取消APPEND_SLASH = False,不过一般不会这样折磨用户。

转换器

正常情况下很多网站都会有很多相似的网址,如果我们每一个都单独开设路由不合理。

django2.X版本及以上提供了动态匹配转换器:path('article/<str:info>/', views.article_page)

其中用小尖括号括起来的<str:info>就可以捕捉路由中这个位置的所有字符串,并以关键字参数的形式传送到对应的功能函数中,这种将一类路由都分组到一个路由配置的方式又称作有名分组

img

我们定义以下视图函数来测试一下参数效果:

def article_page(request, info):
    print(info)
    return HttpResponse(f'article_{info}展示')

img

注意以下易错点

转换器所捕捉的路由会作为关键字参数传入功能函数,于是:

  • 视图函数需要提前定义对应数量的形参
  • 视图函数所定义参数的形参名必须与传入的关键字实参对应

转换器种类

  • str:匹配除路径分隔符外的任何非空字符串。
  • int:匹配0或者任意正整数。
  • slug:匹配任意一个由字母或数字组成的字符串。
  • uuid:匹配格式化后的UUID。
  • path:能够匹配完整的URL路径

转换器不仅可以捕捉匹配的内容,还会将其转换成对应的数据格式,而我们常用的就是str转换器。

正则匹配

在1.X中为默认而在2.X及以上版本也依然可以使用的匹配方式,第一个参数为正则。

这种匹配方式很灵活,但是对于路由匹配而言,并不是匹配到越多的路由就越好,因为也并不一定有对应的界面可以返回给用户。

在2.X版本中,正则匹配关键字为re_path(),1.X版本则为url()

无名分组与有名分组

  • re_path('^test/\d{4}/', views.test)
    如果匹配到test路由后跟四个数字的路由,就转去test函数
  • re_path('^test/(\d{4})/', views.test)
    匹配到后,会将括号中匹配的内容作为位置参数传入test
  • re_path('^test/(?P<year>\d{4})/', views.test)
    匹配到后,会将括号中匹配的内容作为关键字参数传入test

注意:可以分多组进行传参,但是不能混用无名分组和有名分组。

反向解析

静态路由的反向解析

path('login_page/', views.login) 
↓↓↓↓↓↓ 
path('login/', views.login)  # 改变了预设路由

在路由配置完成后,可能存在需要改路由名的情况,但是一旦改动,原本引用这处路由的网址都会失效:

  • templates模板中的html文件的a标签
  • 视图函数的重定向所引用的网址

django考虑这点,提供了反向解析的方法:

通过一个名字可以反向解析出一个结果,通过这个结果可以访问到某个路由。

我们只需要在path函数再添加一个关键字参数name,就相当于给路由起了个别名。

path('login_page/', views.login, name='login_view') 
↓↓↓↓↓↓ 
path('login/', views.login, name='login_view')  # 改变了预设路由

这样我们在可能会失效的链接处都采取反向解析语法,就可以通过login_view解析出路由,用户在去访问链接能对应上了。

反向解析语法

  • urls文件:path('别名', 视图函数, name='别名')
  • html页面:{% url '别名' %}
  • 后端语法:reverse('别名')

这两句语法都会返回一个完整的网址(即域名+路由)的形式。

{% url 'login_view' %}  # 网页上的用法
reverse('login_view')  # 后端的用法

动态路由的反向解析

对于静态路由,我们可以通过一个别名来对应这个路由,而面对可以匹配很多路由的动态路由,通过一个别名来对应显然是不合适的。

动态反向解析语法

  • 路由文件:path('路由/转换器/', 视图函数, name="别名")
  • html页面:{% url '别名' 匹配项 %}
  • 后端语法:reverse('别名', args=(匹配项1,))

例子:

# urls.py
path('modify/<str:modify_id>/', table_crud_views.modify_func, name='modify_func'),

# html页面
<a class="btn btn-info btn-sm" href={% url "modify_func" user_dict.id %}>修改</a>
# 如果这里的id值从字典里取出来是1,则得到 顶级域名/modify/1/

# 后端重定向
def func(request):
    match1 = '一个匹配项'
    return redirect(reverse("modify_func", args=(match1,)))  
# reverse得到的 顶级域名/modify/一个匹配项/

路由分发

django支持每个应用可以有独立拥有路由层。而这样就方便我们每个开发项目的开发完全解耦合,而每个应用独立的路由文件又可以通过路由分发整合到一起。

如我们在app01的文件夹中新建一个urls.py

from django.urls import path  # 需要导入path函数
from app01 import views  # 这个软件的视图导入

urlpatterns = [
    path("login/", views.login)  
]  # 在每个app中路由编写还是一样的

也可以相似的在app02中,也新建一个urls.py编写它的路由:path("login/", views.login)

两个软件的路由可能会冲突,但是用了路由分发就不会避免路由的冲突,视图函数的命名也不必特地避免重复了。

我们在django项目的同名文件夹下的总路由编写:

# django_project/urls.py中
from django.urls import path,include  # 导入path和include方法

urlpatterns = [
    path('app01/', include("app01.urls")),
    path('app02/', include("app02.urls")), 
]  # include函数帮我们将urls文件中的路由全部拼起来了 

img

路由分发后的反向解析

路由分发之后,针对相同的别名能否自动反向解析出不同的应用前缀?

答案是:默认情况下是无法直接识别应用前缀的

解决方案有两种:

  1. 别名定个规范:每个app的路由别名都加app前缀

    # app自己的路由中
    path('index/', views.index, name='app01_index_view')  # app01中
    path('index/', views.index, name='app02_index_view')  # app02中
    
  2. 名称空间:官方提供的方法,能帮我们识别前缀

    # 总路由中
    path('app01/', include(('app01.urls', 'app01'), namespace='app01')),
    path('app02/', include(('app02.urls', 'app02'), namespace='app02')),
    

无论是哪种方法,在反向解析时,其别名的引用都发生了更改,如:

方法1reverse('app01_index_view')和方法2reverse("app01:index_view")

所以这必须是在立项前就要确认好的细节,或者干脆就不要起别名,把路由给定死。

posted @ 2023-06-26 15:15  wwwxxx123  阅读(13)  评论(0编辑  收藏  举报