Django URL路由配置详解:从基础到高级应用
本文深入解析Django URL路由机制,涵盖URLconf模块加载、正则匹配顺序及参数传递方式。详细对比无名与有名分组的传参差异,讲解reverse与url标签实现反向解析,并阐述路由分发、命名空间解决冲突及伪静态URL配置技巧,帮助开发者构建规范的后端路由系统。
一、URL路由基础
URL作为Web服务的入口,负责接收浏览器发送的所有请求。用户通过浏览器发起的请求会被指向特定的URL地址,并由服务器进行响应处理。
在Django项目中配置路由,本质上是定义系统接收哪些URL请求。只有明确配置的路径才会被处理,其他未配置的URL将被忽略。因此,URL路由构成了Web服务对外暴露的API接口集合。
二、Django处理请求流程
系统首先确定要使用的URLconf模块,默认读取settings配置中的ROOT_URLCONF值。如果HttpRequest对象包含urlconf属性(通常由中间件设置),则优先使用该属性指定的模块。
Django加载指定的模块,并提取其中的urlpatterns列表,该列表包含多个django.conf.urls.url()实例。
系统按顺序遍历每个URL模式进行匹配,一旦找到匹配项即停止后续匹配。因此,URL模式的排列顺序至关重要。
匹配成功后,系统导入对应的视图函数或基于类的视图。视图函数将接收以下参数:
- 一个HttpRequest实例,包含请求详情。
- 若正则表达式包含无名分组,捕获的值将作为位置参数传递给视图函数。
- 关键字参数由正则表达式的有名分组生成,但可通过django.conf.urls.url()函数的可选参数kwargs进行覆盖。
若没有任何URL模式匹配,或处理过程中发生错误,系统将调用相应的错误处理视图。
三、简单的路由配置 from django.conf.urls import url
urlpatterns=[ url(正则表达式,view视图函数,参数,别名) ]
示例的URLconf配置如下:
from django.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/([0-9]{4})/$', views.year_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
注意事项:
- 从URL中捕获值时,可以使用圆括号或尖括号定义分组。
- 正则表达式无需添加前导斜杠,因为每个URL路径默认已包含。例如,应写为^articles而非^/articles。
- 正则表达式前的'r'为可选字符,但建议添加。它告知Python该字符串为原始字符串,其中的字符不进行转义处理。
请求示例及匹配结果分析:
/articles/2005/03/ 将匹配列表中的第三个模式。Django调用函数views.month_archive(request, '2005', '03')。 /articles/2005/3/ 不匹配任何URL模式,因为第三个模式要求月份部分必须为两位数字。 /articles/2003/ 将匹配列表中的第一个模式,而非第二个。由于模式按从上到下的顺序匹配,第一个匹配成功的模式会被执行。Django调用函数views.special_case_2003(request)。 /articles/2003 不匹配任何模式,因为所有模式均要求URL以斜杠结尾。 /articles/2003/03/03/ 将匹配最后一个模式。Django调用函数views.article_detail(request, '2003', '03', '03')。
是否启用URL访问地址末尾不带斜杠时自动跳转至带斜杠路径的配置项。
APPEND_SLASH=True
Django settings.py配置文件中默认未显式定义APPEND_SLASH参数,但Django默认将其设为APPEND_SLASH = True。该设置的作用是自动在网址末尾添加'/'。
四、有名分组
有名分组的语法格式为(?P<name>pattern),其中name代表分组名称,pattern代表匹配的正则模式。
使用有名分组重写上述URLconf配置:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
注意:捕获的值将作为关键字参数而非位置参数传递给视图函数。
/articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')。
五、无名分组与有名分组总结
1. 无名分组
按位置传参
分组后,捕获的数据作为位置参数传递至视图函数,因此视图函数需定义对应的形参。
示例:
url: (r'^articles/([0-9]{4})/([0-9]{2})$', views.article_detail)
视图函数: def article_detail(request,*args)
2. 有名分组
按关键字传参
分组后,捕获的数据作为关键字参数传递至视图函数。视图函数需定义形参,且形参名称必须与分组名称一致,参数顺序无关紧要。
示例:
url: (r'^articles/(?P<year>[0-9]{4})/(?P<mounth>[0-9]{2})/$', views.article_detail),
视图函数: def article_detail(request,mounth,year)
注意:建议不要混用有名分组和无名分组。
六、反向解析
在Django项目中,常见需求是获取URL的最终形式,以便嵌入生成的内容(如视图或用户可见的URL)或用于服务器端导航(如重定向)。避免硬编码URL是最佳实践。
Django提供了一种解决方案:只需在URL配置中提供一个name参数,并赋予一个自定义、易记且直观的字符串值。
- 在模板中:使用url模板标签
- 在Python代码中:使用reverse()函数
示例:
URL配置:
- 无参数:url(r'^publishadd111/$',views.publishadd,name='ddd'),
- 无名分组:url(r'^publishadd/([0-9]{4})/([0-9]{2})/$', views.publishadd,name='ddd'),
- 有名分组:url(r'^publishadd/(?P<year>[0-9]{4})/(?P<mounth>[0-9]{2})/$',views.publishadd,name='ddd'),
模板层用法:
- 无参数: {% url 'ddd' %}
- 无名分组: {% url 'ddd' 2018 12 %} 空格隔开,可传递多个值
- 有名分组: {% url 'ddd' 2018 12 %} 或者 {% url 'ddd' year=2018 mounth=12 %}
视图层用法:
from django.shortcuts import reverse
- 无参数: url=reverse('ddd')
- 无名分组: url=reverse('ddd',args=(2018,12,))
- 有名分组: url=reverse('ddd',args=(2018,12,)) 或者 url=reverse('ddd',kwargs={'year':2018,'mounth':12})
七、路由分发
在每个app中独立创建urls.py路由模块,然后在根路由中配置,将属于该app的URL请求转发至相应的urls.py模块。
Django1.1版本的路由分发实现
from django.conf.urls import url,include
示例:
总路由配置:
- -from django.conf.urls import include
- -url(r'^blog/',include('blog.urls')),
- -url(r'^app01/',include('app01.urls')),
各子路由配置url
- app01--url(r'^publish/$', views.publish,name='app01_test'),
- blog--url(r'^blogtest/$', views.test,name='blog_test'),
路由分发使用include()方法,需提前导入。其参数为转发目的地的路径字符串。
关键点:总路由配置末尾不能添加$符号。
若两个不同的app在各自的urlconf中为某条URL设置了相同的name,将导致冲突。为解决此问题,引入了命名空间概念。
八、命名空间
由于name缺乏作用域,Django在反向解析URL时,会在项目全局范围内顺序搜索,一旦找到第一个指定name的URL便立即返回。URL命名空间可确保反向解析到唯一的URL,即使不同app使用了相同的URL名称。
示例:
urls.py配置
url(r'^blog/',include('blog.urls')),
url(r'^app01/',include('app01.urls')),
blog的urls.py
url(r'^blogtest/$', views.test,name='test'),
app01的urls.py
url(r'^publish/$', views.publish,name='test'),
blog的视图函数
def test(request):
url=reverse('test')
return HttpResponse('blog test)
app01的视图函数
def test(request):
url=reverse('test')
return HttpResponse('app01 test)
若不指定命名空间,无论如何查找index,最终都会指向app01的index。
解决方法:在总路由分发时指定命名空间。实现方法很简单,在urlconf文件中添加namespace='xxx'即可。
url(r'^blog/',include('blog.urls',namespace='blog')),
url(r'^app01/',include('app01.urls',namespace='app01')),
在视图函数进行反向解析时,需指定所属的命名空间。
url = reverse('blog:test')
在模板中也需指定命名空间。
{% url 'blog:test'%}
通常不推荐使用命名空间,更推荐的做法是在子路由的name中加入app前缀以区分。
url(r'^publish/$', views.publish,name='app01_test'),
九、伪静态
与真静态URL类似,伪静态通过规则将动态URL伪装成静态网址。
在urls.py文件中手动添加匹配.html后缀的规则即可实现。
url(r'^book/(?P<id>\d+.html)',views.book),
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









