您当前的位置:首页 > 计算机 > 编程开发 > Python

一张图看懂Django路由系统

时间:04-02来源:作者:点击数:
image-20210410220118830

1、传参

a、转换器传参

在传参的部分有一个路由转换器,其作用就是将URL中的路由参数转换为指定的类型,在形式上分为两种,内置路由转换器和自定义路由转换器。

  • 内置路由转换器
    可以显示地指定路由中参数地数据类型,例如<str:phone>,就是指定路由参数phone的数据类型为str。
    内置的路由转换器共有5中:
    • str:匹配出路由分隔符“/”之外的所有非空字符串。(默认)
    • int:匹配0或任何正整数
    • slug:匹配有字母、数字、连字符和下画线(英文形式)组成的URL,例如http://127.0.0.1:8000/blog/type_blog-django
    • uuid:为防止多个url映射到同一个页面中,匹配一个uuid。形如59c08be-b828-11e9-a3b8-408d5c7ffd28。
    • path:匹配任何非空字符串,包括路由分隔符“/” (要与str区分开)

    实例:
    urlpatterns = [
        path('str/<str:str_type>', views.str_converter),  #使用str转换器(str_type是参数名,str_converter是方法名)
        path('int/<int:int_type>', views.int_converter), #使用int转换器,下面一样
        path('slug/<slug:slug_type>', views.slug_converter),
        path('uuid/<uuid:uuid_type>', views.uuid_converter),
        path('path/<path:path_type>', views.path_converter),
    ]
    
  • 自定义路由转换器
    为了匹配一些复杂的参数,如限制路由长度的参数,这时自定义路由转换器应运而生。
    自定义路由转换器就是一个类,其中有属性regex、类方法to_python()、to_url()三者作用如下:
    • regex:设置匹配规则
    • to_python():将匹配到的字符串转换成要传递到视图中的类型。
    • to_url():将python数据类型转换为URL中使用的字符串。
    实例:
    在项目中的一个应用hello中新建一个converter.py文件,代码如下:
    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):              #转换字符串z
            return value
    register_converter(MyConverter, 'mobile') #注册自定义路由器,mobile为自定义路由器的名称(在urlpatterns中会用到)
    
    在同级文件夹中的urls.py文件中:
    from django.urls import path
    from hello import converter,views
    urlpatterns = [
        path('mobile/<mobile:phone_num>/', views.show_mobile)
    ]
    
    在同级文件夹中的views.py文件中:
    from django.http import HttpResponse
    def show_mobile(request, phone_num):
        return HttpResponse(f'手机号为:{phone_num}')
    
    image-20210410213911545
b、正则表达式传参

在URL中包含文件路径、工牌号这类不规则的信息时,则使用路由转换器无法更好的匹配URL模式,此时可以使用正则表达式定义路由模式。形式上分为两种:命名正则表达式,未命名正则表达式。

将urls.py中的urlpatterns中的path()换为re_path()

re_path(route, view, kwargs=None, name=None)    #route为正则表达式

实例:

from django.urls import re_path
urlpatterns = [
    re_path(r'^index/$', views.index, name='index'),   #r为原生字符串,以免发生转义
    re_path(r'^bio/(?P<username>\w+)/$', views.bio, name='bio'),
    ...
]
  • 命名正则表达式(命名参数)
    形如(?P<name>pattern)
    P是固定格式
    <name> : 须与视图中的形参保持一致
    pattern:正则表达式
re_path(r'^index/(?P<name>\w+)/$', views.site),

​ 若url为/index/itcast/会匹配该模式,则调用views.site()视图,并将捕获的参数“name=itcast”传递给视图。

  • 未命名正则表达式(位置参数)

​ 只通过()来捕获参数

re_path(r'num/(\d+)/', views.number),

​ 若URL为/num/123/,此条URL与之匹配,路由系统调用views.number()视图,并将URL中的参数123传递给视图。

c、向视图传递额外参数

path()函数、re_path()函数允许向视图传递额外参数,这些参数存放在一个字典类型的数据中,该数据的键代表参数名,值代表参数值。

例如在hello应用中的urls.py文件中定义如下URL模式:

path('blog-list/', views.blog, {'blog_id':3}),

当路由系统匹配到以上URL模式时,会调用views.blog()视图,并向该视图传递值为3的参数blog_id。

在views.py文件中:

def blog(request, blog_id):
    return HttpResponse(f'参数blog_id值为:{blog_id}')
image-20210411105938360

也可以向include()中传递额外参数:

向include()函数传递参数时,include()函数会将参数传递到被引入的URLconf中,被引入的URLconf会将参数传递给本模块每个URL对应的视图,例如:

#根URLconf
urlpatterns = [
    path('blog/', include('hello.urls'), {'blog_name':'Django'}),
]
#hello应用URLconf
urlpatterns = [
    path('archive/', views.archive),
    path('about/', views.about),
]

上例中hello应用中的URLconf会将blog_name参数分别传递到archive和about视图中。

2、路由分发

一个Django项目中会包含多个应用,每个应用都可以设置多个URL,如果将项目所有的URL都保存在根URLconf中,那么URLconf会变得非常臃肿,不利于维护。而每个应用都可以封装在本应用的URLconf中,在根URLconf使用urls模块的include()函数将应用中的URLconf导入即可实现路由分发,该方式降低了URLconf与根URLconf的耦合度。

include引入

降低URLconf与根URLconf的耦合度

在helloworld项目中,引入应用hello的URLconf:

image-20210410224156062

然后再hello.urls中定义与该应用相关的url

模式列表引入

会使根URLconf比较臃肿,不利于降低URLconf与根URLconf的耦合度

在helloworld项目中的根URLconf中:

from django.contrib import admin
form django.urls import path,include
from hello import views
hello_extra_patterns = [
    path('reports/', views.report),
    path('reports/<int:id>/', views.show_num),
]
urlpatterns = [
    path('admin', admin.site.urls),
    path('hello', include(hello_extra_patterns)),
]

访问/hello/reports/时,路由系统首先在helloworld项目的根URLconf进行URL匹配,匹配到/hello/后路由系统遍历URL模式变量hello_extra_patterns中的元素,继续匹配URL的其余部分。

3、URL命名与命名空间

URL命名

因为url是经常变化的。如果在代码中写死可能会经常改代码。给url取个名字,以后使用url的时候就使用他的名字进行反转就可以了,就不需要写死url了。

urlpatterns = [
    path('user-login/', views.login, name='login'),   #将user-login/命名为login,也就是说login为该url的模式名称
]
reverse()反向解析URL

使用reverse()反向解析URL,直到URL被访问时,Django服务器才会获取具体的URL。

reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)

参数说明:

viewname:URL模式名称或可调用的视图对象

urlconf:包含url模式中的URLconf模块

args:传递给URL的列表类型的参数

kwargs:传递给url的字典类型的参数

current_app:当前视图所属的应用

实例:

在hello.urls中添加:

path('url-reverse/',views.get_url,name='url'),

在hello中的views.py中添加:

from django.shortcuts import reverse
def get_url(request):
    return HttpResponse(f"反向解析的url为:{reverse('url')}")
image-20210411114954033
应用命名空间

Django的多个应用中可能包含同名的url,为了避免反向解析URL时产生混淆,可以使用命名空间区分不同应用。

在helloworld项目中创建hello2应用并在此应用中新建urls.py,下面通过hello应用与hello2应用来演示命名空间的使用:

在helloworld项目的URLconf中添加:

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

在hello2应用的urls.py文件中添加:

from hello2 import views
app_name = 'hello2'     #设置hello2应用命名空间
urlpatterns = [
    path('login/', views.login, name='login'),
]

在hello应用的urls.py文件中添加URL模式:

path('login/', views.login, name='login'),

分别在hello应用和hello2应用中的views.py中定义login()视图:

#hello
def login(request):
    return HttpResponse(f"反向解析的url为:{reverse('hello:login')}")  #命名空间:URL模式名称
#hello2
def login(request):
    return HttpResponse(f"反向解析的url为:{reverse('hello2:login')}")

其实作用就是告诉Django各自找各自应用中的视图,不要乱找。当然你也可以指定某一其他应用中的URL模式名称,让他反向解析。

image-20210411141708419
实例命名空间

为了解决多个应用指向同一个urls.py而产生的混淆。

Django允许多个应用的URLconf指向同一个应用的URLconf,即在项目helloworld的根URLconf中定义:

path('path-one/', include('app04.urls')),
path('path-two/', include('app04.urls')),

app04.urls:

path('index/', views.url_path, name='url_path'),

app04.views:

def url_path(request):
    return HttpResponse(f"当前url为:{(reserse('app04:url_path'))}")

此时访问/path-one/index/或是/path-two/index/都响应为/path-one/index/,这是因为反向解析URL都满足根URLconf的第一条URL规则。

image-20210411152706173
image-20210411152643471

为了解决这个问题,我们加入namespace来做区分。

在helloworld项目的根URLconf中的URL设置实例命名空间:

urlpatterns = [
    path('path-one/', include('app04.urls', namespace='one')),  #设置命名空间为one
    path('path-two/', include('app04.urls', namespace='two')),
]

修改app04.views为:

def url_path(request):
    return HttpResponse(f"当前的url为:{(reserse('app04:url_path', current_app=request.resolver_match.namespace))}")
# request.resolver_match.namespace获取当前URL实例的命名空间
image-20210411152336946
image-20210411152411511

这时就能很好的解决上面的问题。

小结:

应用命名空间和实例命名空间都是为了解决同名时反向解析的混乱。应用命名空间是为了解决当多个应用的url名一样时,用应用命名作以区分防止反向解析混乱;实例命名空间时为了解决当多个应用指向同一个应用的URLconf时,用实例命名(namespace)作以区分,就知道是哪个应用发起的请求了,从而防止反向解析混乱。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门