内容导读
- 认识路由系统
- 路由转换器
- 使用正则表达式匹配URL
- 路由分发
- 向视图传递额外参数
- URL命名与命名空间
一、认识路由系统
通过URL(Uniform Resource Locator,统一资源定位符)可以访问互联网上的资源——用户通过浏览器向指定URL发起请求,Web服务器接收请求并返回用户请求的资源,因此可以将URL视为用户与服务器之间交互的桥梁。
作为一个优秀的Web框架,Django提供了配置URL的路由系统,通过该路由系统,开发人员可以设计出简洁、优雅的URL。
1、HTTP请求处理流程概述
Django框架的路由系统接收用户通过浏览器发来的HTTP请求,为请求分派视图,以处理请求并返回响应。结合Django项目进行分析,这一处理HTTP请求的具体流程如下图所示。
2、URL配置示例
一个项目允许有多个urls.py,但Django需要一个urls.py作为入口,这个特殊的urls.py就是根URLconf(根路由配置),它由settings.py文件中的ROOT_URLCONF指定。
ROOT_URLCONF = 'chapter02.urls'
以上通过ROOT_URLCONF指定了chapter02目录下的urls.py作为根URLconf。
为保证项目结构清晰,开发人员通常在Django项目的每个应用下创建urls.py文件,在其中为每个应用配置子URL。路由系统接收到HTTP请求后,先根据请求的URL地址匹配根URLconf,找到匹配的子应用,再进一步匹配子URLconf,直到匹配完成。
二、路由转换器
2.1 内置路由转换器
内置路由转换器可以显式地指定路由中参数的数据类型,Django中内置了5种路由转换器,分别为str、int 、slug 、uuid 和path。
str:匹配任何非空字符串,但不包含路由分隔符“/”。如果URL中没有指定参数类型,默认使用该类型。
int:匹配0或任何正整数。
slug:匹配由字母、数字、连字符和下画线组成的URL。
uuid:匹配一个uuid。为了防止多个URL映射到同一页面中,该转换器必须包含连字符,且所有字母均为小写。
path:匹配任何非空字符串,包括路由分隔符“/”。
在项目的urls.py中分别使用前面的5种路由转换器定义URL模式,示例如下:
urlpatterns = [
path('str/<str:str_type>', views.str_converter), # 使用str转换器
path('int/<int:int_type>', views.int_converter), # 使用int转换器
path('slug/<slug:slug_type>', views.slug_converter), # 使用slug转换器
path('uuid/<uuid:uuid_type>', views.uuid_converter), # 使用uuid转换器
path('path/<path:path_type>', views.path_converter), # 使用path转换器
]
2.2 自定义路由转换器
虽然内置的路由转换器能够处理绝大部分应用场景,但在实际开发中可能需要匹配一些复杂的参数,如限制路由长度的参数,这时就需要开发人员自定义路由转换器。
自定义路由转换器本质上是一个类,这个类需包含类属性regex、类方法to_python()和to_url(),其中regex设置匹配规则,to_python()方法将匹配到的字符串转换成要传递到视图中的类型;to_url()方法将Python数据类型转换为URL中使用的字符串。
自定义路由转换器定义完成之后,需通过urls模块中的register_converter()函数注册到Django框架中。register_converter函数的语法格式如下:
register_converter(converter, type_name)
converter:接收自定义的路由转换器类。
type_name:表示在URL中使用的路由转换器名称。
(1)创建Django项目chapter02,在此项目中创建应用app01,在应用app01中新建converter.py文件,在该文件中自定义路由转换器类MyConverter,并使用register_co
nverter()函数注册自定义路由转换器。定义与注册自定义路由转换器的代码具体如下:
from django.urls import register_converter
class MyConverter:
regex = '1[3-9]\d{9}' # 匹配规则
def to_python(self, value):
return value
def to_url(self, value):
return value
register_converter(MyConverter, 'mobile') # 注册自定义的路由转换器
(2)路由转换器定义好之后,在app01应用的urls.py文件中导入转换器所在文件并使用自定义的路由转换器,代码如下:
from django.urls import path
from app01 import converter,views # 导入应用中的converter
urlpatterns = [
path('mobile/<mobile:phone_num>/',views.show_mobile)
]
(3)在app01应用中定义moblie视图,在页面中呈现手机号。示例如下:
from django.http import HttpResponse
def show_mobile(request, phone_num):
return HttpResponse(f'手机号为:{phone_num}')
(4)启动chapter02项目,访问http://127.0.0.1:8000/mobile/13000000000/,此时,因为 URL中的手机号13000000000匹配自定义的路由转换器mobile,所以Django能够调用views.py中的show_mobile视图,在页面中呈现手机号,如下图所示。
三、使用正则表达式匹配URL
若URL中包含如文件路径、工牌号这类不规则的信息,则使用路由转换器无法很好地匹配URL模式,此时可以使用正则表达式定义路由模式。若要使用正则表达式匹配URL,则可以使用re_path()函数,该函数语法格式如下:
re_path(route, view, kwargs=None, name=None)
re_path()函数的参数route接收一个正则表达式,其余参数与path()函数中参数的作用相同。
使用re_path()函数匹配URL,示例如下:
from django.urls import re_path
urlpatterns = [
re_path(r'^index/$', views.index, name='index'),
re_path(r'^bio/(?P<username>\w+)/$', views.bio, name='bio'),
...
]
以上示例使用原生字符串为re_path()函数的参数route传参,以免因正则表达式包含转义字符而产生转义。
使用re_path()函数匹配包含正则表达式的URL有两种方式。
1. 命名正则表达式
命名正则表达式格式为:(?P<name>pattern),其中name表示分组名,pattern表示匹配的正则表达式。URL匹配成功后,捕获到的参数会作为关键字参数传递给对应的视图,因此视图中的形式参数必须和正则表达式中的分组名相同。
2. 未命名正则表达式
若正则表达式只通过小括号“()”来捕获URL的参数,但未为其命名,则它是一个未命名正则表达式,此时捕获的参数并将其以位置参数形式传递给对应视图。
四、路由分发
通常情况下,一个Django项目中会包含多个应用,每个应用都可以设置多个URL,如果将项目所有的URL都保存在根URLconf中,那么URLconf会变得非常臃肿,不利于维护。
Django允许每个应用将URL封装到本应用的URLconf中,在根URLconf使用urls模块的include()函数将应用中的URLconf导入即可实现路由分发,路由分发的使用降低应用的URLconf与根URLconf的耦合度。
使用include()函数实现路由分发有以下2种方式:
include(module,namespace = None) # 1.引入应用URLconf
include(pattern_list) # 2.引入URL模式列表
module:用于指定URLconf模块。
namespace:用于指定URL模式实例的命名空间。
pattern_list:用于指定一个可迭代的path或re_path实例列表。
五、向视图传递额外参数
path()函数、re_path()函数允许向视图传递额外参数,这些参数存放在一个字典类型的数据中,该数据的键代表参数名,值代表参数值。re_path()函数与path()函数传递额外参数方式相同,以path()函数为例介绍如何向视图传递额外参数。
使用path()函数的第三个参数可以向视图传递额外参数。例如,在app02应用的urls.py文件中定义如下URL模式:
path('blog-list/',views.blog,{'blog_id':3}),
路由系统匹配到以上URL模式时,会调用向blog视图,并向该视图传递值为3的参数blog_id。
向include()中传递额外参数:
path()函数除了可以向视图中传递额外参数,还可以向include()函数传递额外参数。向include()函数传递参数时,include()函数会将参数传递到被引入的URLconf中;被引入的URLconf会将参数传递给本模块每个URL对应的视图。
# 根URLconf
urlpatterns = [
path('blog/', include('app02.urls'),
{'blog_name':'Django'}),
]
# app02应用URLconf
urlpatterns = [
path('archive/', views.archive),
path('about/', views.about),
]
六、URL命名与命名空间
6.1 URL命名
项目开发过程中一个URL可能经常发生变化,例如项目初期用户登录使用的URL为/uesr-logon/,但在项目后期需要将用户登录URL更换为/login/,这意味着只要涉及/user-logon/的URL都需要更改为/login/。将项目中的/user-logon/替换为/login/会是一个重复且枯燥的过程。此时可通过URL命名来简化这一处理。
在path()函数或re_path()函数中使用参数name为URL命名。
urlpatterns = [
path('user-login/', views.login,name='login'),
]
以上示例代码将路由/user-login/命名为“login”,通过该名称便可调用login视图,若后期需要修改访问login视图的URL,只需修改path()或re_path()函数中的URL即可。
6.2 使用reverse()反向解析URL
使用Django开发应用时可以直接使用URL,但此种方式URL与项目耦合度较高,如果urls.py中修改了某个页面的URL,那么视图或模板都需要修改,在维护或更新项目过程中都有可能出现错误。
Django使用urls模块中的reverse()函数实现反向解析,直到URL被访问时,Django服务器才会获取具体的URL。reverse()函数的语法格式如下:
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
viewname:URL模式名称或可调用的视图对象。
urlconf:一个包含URL模式的URLconf模块。
args:传递给URL的列表类型的参数。
kwargs:传递给URL的字典类型的参数。
current_app:当前视图所属的应用。
注意:reverse()函数中参数args与kwargs不能同时使用。
6.3 应用命名空间
Django的多个应用中可能包含同名的URL,为了避免反向解析URL时产生混淆,可以使用命名空间区分不同应用。
只需在应用的urls.py文件中定义app_name变量,便可指定当前应用的命名空间
其格式:应用命名空间:URL名称
(1)将app04应用的URLconf添加到chapter02项目的根URLconf中,具体如下:
app_name = 'app03' # 在app03应用urls.py文件中设置
app_name = 'app04' # 在app04应用urls.py文件中设置
(2)在app04应用的urls.py文件中定义URL模式,具体如下:
def login(request):
return HttpResponse(f"反向解析的url为:{reverse('app03:login')}")
(3)在app03应用的urls.py文件中追加URL模式,具体如下:
path('login/',views.login,name='login'),
(4)分别在app03应用与app04应用的views.py文件中定义login()视图,具体如下:
def login(request):
return HttpResponse(f"反向解析的url为:{reverse('login')}")
(5)当访问的URL为/app03/login/时,页面响应结果如下图所示。
由上可知,访问的地址为app03/login/,而页面显示的地址为app04/login/,显然页面响应的结果与实际访问的地址不同,这不符合我们的需求,此时可以通过设置应用命名空间来解决以上问题。
(6)在app03与app04应用的urls.py文件中分别设置变量app_name的值,具体如下:
app_name = 'app03' # 在app03应用urls.py文件中设置
app_name = 'app04' # 在app04应用urls.py文件中设置
(7)将login()视图中的反向解析按照“reverse(‘应用命名空间名称:login’)”格式修改,具体如下(以app03应用login()视图为例):
def login(request):
return HttpResponse(f"反向解析的url为:{reverse('app03:login')}")
(8)再次访问/app03/login/,页面响应结果如下图所示。
由上图可知,通过设置应用命名空间可以正确区分不同应用中的同名的URL。
6.4 实例命名空间
Django中允许多个应用的URLconf指向同一个应用的URLconf,若在视图中使用应用命名空间实现反向解析时出现URL匹配混淆,为解决这个问题,可设置URL实例命名空间来解决URL匹配混淆的问题。
实例命名空间在include()函数中设置参数namespace的值。格式如下:
path('index/',include('app04.urls',namespace='app04'))
更多精彩内容请关注本站!