在传参的部分有一个路由转换器,其作用就是将URL中的路由参数转换为指定的类型,在形式上分为两种,内置路由转换器和自定义路由转换器。
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),
]
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}')
在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'),
...
]
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传递给视图。
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视图中。
一个Django项目中会包含多个应用,每个应用都可以设置多个URL,如果将项目所有的URL都保存在根URLconf中,那么URLconf会变得非常臃肿,不利于维护。而每个应用都可以封装在本应用的URLconf中,在根URLconf使用urls模块的include()函数将应用中的URLconf导入即可实现路由分发,该方式降低了URLconf与根URLconf的耦合度。
降低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的其余部分。
因为url是经常变化的。如果在代码中写死可能会经常改代码。给url取个名字,以后使用url的时候就使用他的名字进行反转就可以了,就不需要写死url了。
urlpatterns = [
path('user-login/', views.login, name='login'), #将user-login/命名为login,也就是说login为该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)作以区分,就知道是哪个应用发起的请求了,从而防止反向解析混乱。