2 URL参数传递
path()函数、re_path()函数允许向视图传递额外参数,这些参数存放在一个字典类型的数据中,该数据的键代表参数名,值代表参数值。re_path()函数与path()函数传递额外参数方式相同,以path()函数为例介绍如何向视图传递额外参数。
2.3.1 传递URL中获取的数据
使用path()函数解析URL时,可以在URL模式中使用“<变量名>”来捕获URL路径中的数据,示例代码如下。
path('test/<urldata>',views.getdata),
Django在解析URL时,会将路径“/test/xxx/”的“xxx”作为值传递给变量urldata。同时还需要再视图函数getdata()中定义一个名为urldata的参数。
下面的实例在项目视图文件testurl\testurl\views.py中添加一个视图函数,将URL中获取的数据输出到响应页面。
具体步骤如下。
- 在“testurl\testurl\views.py”中添加一个视图函数.
def getdata(request,data2):
return HttpResponse('这里的路径是:%s' %(data2))
- 打开“testurl\testurl\urls.py”文件,添加访问视图函数getdata的URL配置,代码如下。
....
from . import views
urlpatterns = [
....
path('test/<data2>',views.getdata),
]
- 在浏览器中访问“http://127.0.0.1:8000/test/ilovedjango”,结果如图2-7所示。
图 2-7 获取URL中的数据输出到页面
本例中的URL路径“/test/ilovedjango”与URL模式“test/<data2>”匹配,“ilovedjango”作为数据传递给视图函数的参数data2。
2.3.2 路径转换器
内置路由转换器可以显式地指定路由中参数的数据类型,Django中内置了5种路由转换器,分别为str、int 、slug 、uuid 和path 。
- str:匹配任何非空字符串,但不包含路由分隔符“/”。如果URL中没有指定参数类型,默认使用该类型。
- int:匹配0或任何正整数。
- slug:匹配由字母、数字、连字符和下画线组成的URL。
- uuid:匹配一个uuid。为了防止多个URL映射到同一页面中,该转换器必须包含连字符,且所有字母均为小写。
- path:匹配任何非空字符串,包括路由分隔符“/”。
下面的例子在项目视图文件“testurl\testurl\views.py”中添加定义两个视图函数,使用常用的str和int路径转换器。
具体步骤如下。
- 在项目视图文件“testurl\testurl\views.py”中添加如下代码。
def index_world(reqeust,world):
return HttpResponse(world) #定义路由转换器为str路由转换器
def index_num(request,number):
return HttpResponse('这里的路径是:%s' %(number)) #定义路由转换器为int路由转换器
- 打开“testurl\testurl\urls.py”文件,添加访问视图函数index_world和index_num的URL配置,代码如下。
....
from . import views
urlpatterns = [
....
path('world/<str:world>',views.index_world), #str路由转换器
path('num/<int:number>',views.index_num), #int路由转换器
]
- 在浏览器中访问“http://127.0.0.1:8000/world/abc123”,结果如图2-8所示。
图 2-8 获取URL中的字符串
- 因为使用了str转换器,匹配任何非空字符串,当输入127.0.0.1:8000/world/abc123时,能够成功响应,当输入127.0.0.1:8000/num/abc123,结果如图2-9所示。因为int路径转换器 只是匹配0或任何正整数。而传递的参数abc123显然不符合。
图 2-9 获取URL中数字失败
- 在浏览器中访问“http://127.0.0.1:8000/num/123”,结果如图2-10所示。这里传递的参数123符合int转换器类型。
图 2-10 获取URL中数字成功
2.3.3 传递附加参数
使用path()或re_path()的第三个参数可以向视图传递额外参数。例如在“testurl\testurl\urls.py”文件中定义如下URL模式。
path('<data>',views.getextrdata, {'ex':'python web'}),
路由系统匹配到以上URL模式时,会调用views.getextrdata函数,并向该函数传递值为‘python web’的参数ex。
URL模式中向视图传递了参数,那么视图必然定义了接收该参数的形参。在“testurl\testurl\urls.py”视图中,添加如下视图。
def getextrdara(request,data,ex):
return HttpResponse('从URL获取的参数是:%s,附加数据为:%s '%(data,ex))
此使访问“http://127.0.0.1:8000/hello”,结果如图2-11所示。“hello”为从URL获取的参数,“python web”为视图将参数书ex的值签到在响应信息返回的数据。
图 2-11 传递URL中获取的数据与附加参数
2.4 反向解析
使用Django开发应用时可以直接使用URL,但此种方式URL与项目耦合度较高,如果urls.py中修改了某个页面的URL,那么视图或模板都需要修改,在维护或更新项目过程中都有可能出现错误。Django使用urls模块中的reverse()函数实现反向解析。
反向解析URL可以通过URL模式的名称或视图函数名称来获得访问视图函数的URL。reverse()函数的语法格式如下。
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
- viewname:必选参数,URL模式名称或可调用的视图对象。
- urlconf:可选参数,一个包含URL模式的URLconf模块。
- args:可选参数,传递给URL的列表类型的参数。
- kwargs:可选参数,传递给URL的字典类型的参数。
- current_app:可选参数,当前视图所属的应用。
2.4.1 反向解析命名URL模式
可用reverse(“URL模式名称”)语句来获得URL。
下面的例子在第一单元创建的项目视图文件“helloworld\helloworld\views.py”添加视图函数geturl。
- 修改“helloworld\helloworld\views.py”,添加定义视图函数,其中“two”为hello视图函数的路由命名。
from django.http import HttpResponse
from django.urls import reverse
def hello(request):
return HttpResponse("Hello world ")
def geturl(request):
return HttpResponse('反向解析的url地址是: %s' %reverse('two'))
- 在项目URL配置文件“helloworld\helloworld\urls.py”中添加URL映射,并且为Path(‘', views.hello)路径添加命名。
from django.conf.urls import url
from . import views
urlpatterns = [
Path(‘helloworld', views.hello,name='two'),
path('rev/ccit123',views.geturl,name='one'),
]
- 在浏览器中访问http://127.0.0.1:8000/rev/ccit123,页面的输出结果如图2-12所示。
图 2-12 反向解析命名URL模式
2.4.2 反向解析视图函数
可以使用视图函数名称作为reverse()函数参数来执行反向解析,这与反向解析命名URL模式类似。
下面的例子在第一单元创建的项目视图文件“helloworld\helloworld\views.py”修改视图函数geturl。
- 修改“helloworld\helloworld\views.py”,将geturl中reverse ('URL模式名称'))更改为reverse(geturl)),geturl为视图名称。在页面中输出浏览器请求的URL路径会返回geturl配置的路由路径。
def geturl(request):
# return HttpResponse('反向解析的url地址是: %s' %reverse('two')) 方法一
return HttpResponse('反向解析的url地址是: %s' % reverse(geturl))
- 在项目URL配置文件“helloworld\helloworld\urls.py”不需要更改,在浏览器中访问http://127.0.0.1:8000/rev/ccit123,页面中的输出结果如图2-13所示。
图 2-13 用视图函数名作为参数反向解析URL
2.4.3 在模板中反向解析URL
在模板中也可以用反向解析来获得访问视图函数的URL,基本格式如下。
{% url URL模式名称 参数 %}
下面的例子在项目视图文件“helloworld\helloworld\views.py”添加视图函数,在模板中使用反向解析函数获得访问视图函数的URL。
具体步骤如下。
- 在“helloworld\helloworld\”文件夹下新建templates文件,并在其中创建helloworld.html文件,添加下面的代码定义模板。
{% url URL模式名称 参数 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>你好</h1>
{% url 'two'%}
<div>
<p><a href="{% url 'two'%}">点击我</a> </p>
</div>
<div>
<p><a href="{% url 'two'%}">点击我</a> </p>
</div>
<div>
<p><a href="{% url 'two'%}">点击我</a> </p>
</div>
<div>
<p><a href="{% url 'two'%}">点击我</a> </p>
</div>
<hr>
</body>
</html>
- 修改“helloworld\helloworld\views.py”,添加下面的代码定义视图函数。
def index2(request,):
return render(request,'helloworld.html')
- 为index2视图函数配置路由,代码如下。
path('second/',views.index2),
- 在浏览器中访问http://127.0.0.1:8000/second/,页面输出结果如图2-14所示。可以发现 HTML中定义的{% url 'two'%}变量在页面中显示为index函数的路由路径。
图 2-14 在模板中反向解析URL
- 在helloworld.html文件中,将变量放在了超链接中,点击其中一条连接,页面调转到了helloworld项目页面,如图2-15所示。
图 2-15 hellowold页面
由此可以实例可以发现,在实际开发中,当前端页面出现较多相同链接。可以使用在模板中反向解析URL的方式定义各个链接,后期需要更改URL地址时,开发人员不需要更改前端超链接网址,只需要在路由配置时更改route即可。例如,将helloworld/路径更改为hw/,只需要在urls.py中修改index函数的路由配置路径。
#path('helloworld/',views.index,name='one'),
path('hw/',views.index,name='one'),
- 再次在浏览器中访问http://127.0.0.1:8000/second/,页面输出结果如图2-16所示。
图 2-16 在模板中反向解析URL
2.5 URL命名空间
url的命名空间往往是和反向解析模式结合使用的,当涉及到多个应用分配路由,且函数或URL名可能出现重名的情况,我们需要给它们设置命名空间来帮助 reveser函数来避免混淆。正确使用URL命名空间,可对项目中的应用进行多次部署,级可通过不同的URL访问同一个应用。
Django将URL命名空间分为应用命名空间和实例命名空间。
2.5.1 应用命名空间
应用命名空间指正在部署的应用的URL命名空间名称。一个应用的多个实例可共享同一个应用命名空间,也可配置多个应用命名空间。下面通过一个实例来讲解应用命名空间的功能和配置方式。
实例:在项目文件app02下创建两个应用app03和app04,分别为他们配置路由表,路径名一样,命名也一样。如图2-17和2-18所示
图 2-17 app03应用路由表
图 2-18 app04应用路由表
在根路由表app02\app02\urls.py下使用include()函数进行路由分发。如图2-19所示。应用app03的路径应为app03/login,应用app04的路径应为app04/login
图 2-19 根路由路径设置
分别在app03和04应用中中创建视图函数login:业务逻辑完全相同,通过视图函数名来逆向解析出路由路径。如图2-20所示。
图 2-20 app03与app04视图定义
启动开发服务器,输入app03/login查看反向解析的数据,正常情况下逆向解析返回的数据应该是app03/login,实际显示结果如图2-21所示。逆向解析数据错误。
图 2-21 通过视图函数名反向解析app03路径
分析出错原因,不难发现,就是文件开头提到的,当涉及到多个应用分配路由,且函数或URL名可能出现重名的情况, reveser函数出现了混淆。因此我们需要给它们设置命名空间来帮助 reveser函数来避免混淆。应用命名空间使用方式如下。分别在app03\urls.py和app04\urls.py中输入app_name=‘应用命名空间名’来设置应用命名空间名。
|
修改应用app03与app04视图函数,在反向解析reverse中输入“应用名:视图函数名”的形式。
![]() 图 2-24 app03视图函数设置 | ![]() 图 2-25 app04视图函数设置 |
应用命名空间配置完成后登录开发服务器在url中输入http://127.0.0.1:8000/app04/login和http://127.0.0.1:8000/app03/login查看反向解析是否成功。
![]() 图 2-26 app03视图函数设置 | ![]() 图 2-27 app04视图函数设置 |
比较2-26和2-27可知,通过设置应用命名空间可以正确区分不同应用中同名的URL。
2.5.2 实例命名空间
实例命名空间是应用特定实例的URL命名空间。实例命名空间名称在整个项目中必须唯一。Django中允许多个应用的URL指向同一个应用的URLconf,在视图中使用应用命名空间实现反向解析时出现URL匹配混淆,例如项目chapter02的根URLconf中定义了URL模式。
path('path-one/',include(‘app04.urls’)),
path('path-two/',include(‘app04.urls’)),
该项目app04应用的urls.py定义了URL模式,具体如下。
path('index/',include(‘app04.urls’,views.url_path,name=’url_path’),
该项目app04应用的views.py定义了url_path()视图,具体如下。
def url_path(request):
Return HttpResponse(f”当前url为:{reverse(‘app04:url_path’)}”)
此时,访问/path-one/index/或/path-two/index/,页面中均响应“当前url为:/path-one/index/”,这是因为反向解析URL都满足根URLconf中第一条URL规则。页面响应结果如图2-28所示。
图 2-28 响应结果
但期望视图函数能根据URL的具体路径响应不同的信息。为解决这个问题,可以设置URL实例命名空间。实例命名空间在include()函数中设置参数namespace的值,其格式如下。
path('index/',include('app04.urls',namespace='app04')),
在chapter02项目的根URLconf中为URL设置实例命名空间,具体如下。
urlpatterns = [
path('path-one/',include('app04.urls',namespace=’one’)),
path('path-two/',include('app04.urls',namespace=’two’)),
]
修改app04应用url_path()视图,在该视图中通过request.resolver_match.namespace获取当前URL实例的命名空间,区分当前调用URL的应用,使反向解析能获得正确的结果。实例如下。
def url_path(request):
return HttpResponse(f”当前url为:{(reverse(‘app04:url_path’,current_app=request.
resolver_match.namespace))}”)
此使,访问/path-one/index/页面响应“当前url为:/path-one/index/”;访问/path-two/index/页面响应“当前url为:/path-two/index/”。设置实例命名空间响应结果。如图2-29所示。
图 2-29 响应结果
2.6本章小结
本章介绍了Django框架中的路由系统,包括如何处理Http请求。路由转换器、如何利用正则表达式配置URL、通过include()函数实现路由分发、向视图传递额外参数、URL命名与命名空间、反向解析URL。Django的URL分发机制将用户访问的URL与实现输出页面的视图函数、模板、模型等隔离,也在一定程度上保障了站点安全。