Python Web开发Django框架路由系统指导

内容导读

  1. 认识路由系统
  2. 路由转换器
  3. 使用正则表达式匹配URL
  4. 路由分发
  5. 向视图传递额外参数
  6. 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'))

更多精彩内容请关注本站!

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值