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}')
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}')
也可以向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:
然后再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')}")
应用命名空间
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模式名称,让他反向解析。
实例命名空间
为了解决多个应用指向同一个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规则。
为了解决这个问题,我们加入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实例的命名空间
这时就能很好的解决上面的问题。
小结:
应用命名空间和实例命名空间都是为了解决同名时反向解析的混乱。应用命名空间是为了解决当多个应用的url名一样时,用应用命名作以区分防止反向解析混乱;实例命名空间时为了解决当多个应用指向同一个应用的URLconf时,用实例命名(namespace)作以区分,就知道是哪个应用发起的请求了,从而防止反向解析混乱。