Django路由系统
简洁优雅的URL
结构是高质量Web
应用程序的象征。Django
允许开发人员设计任何形式的URL
,这在早期的网站中是不可想象的。早期的网站通常会有很长的一串URL
,而且通常U虹还会包括一些无用信息,如.aspx
、.php
等。
为了给应用程序设计URL
,开发人员需要开发一个Python
模块,这个模块就是URL的配置信息,通常我们将这个配置模块叫作URLconf.
。这个模块是一个纯粹的Python
脚本,它包含了URL
表达式与Python
方法之间的映射,这里的Python
方法就是Django
应用中的视图方法。前面示例中的mysite/urls.py
和polls/urls.py
就是两个URLconf
实例。
Django
处理HTTP
请求的流程
当用户发起一个HTTP
请求时,Django
就会按照以下逻辑对请求进行处理:
- 确定
URL
根配置位置,通常URL
根配置在ROOT_URLCONF
中设置。 - 加载配置信息,在配置信息中查找
urlpatterns
。 - 按顺序检索
urlpatterns
中的所有URL
模式字符串,井定位在第一个与URL
匹配的URL
模式字符串。 - 当检索到匹配的
URL
模式字符串后,调用对应的视图方法,并传递以下参数给视图方法:- 一个
HttpRequest
对象实例。 - 如果匹配的
URL
模式字符串不包含任何组,那么匹配的信息会作为位置参数传递给视图。 - 如果
URL
模式字符串中的参数给定了参数名,那么匹配的信息会作为命名参数传递给视图。
- 一个
- 如果在
URLconf
中没有找到任何匹配的URL
模式字符串,或者出现其他任何错误,Django
将会调用一个用于处理错误信息的视图。
URLconf
示例
下面是一个URLconf
的简单示例。
1 | from django.urls import path |
示例解读:
- 函数
path
的第一个参数是一个URL
模式字符串,用于匹配URL
。 - 函数
path
的第二个参数是用于处理URL请求的视图函数。 - 使用尖括号提取
URL
中的参数,如<int:year>
。 - 可使用类型转化器对参数类型进行转换,如
int:
会将从URL
中捕获的值转换为数值类型,如果没有指定类型转换器,如<year>
,则任何不包含/的字符串都会被提取。 URL
模式字符串不需要以/开头。
应用场景:
- 发送向
/articles/2005/03/
的请求将会与第三个URL模式字符串匹配成功,匹配成功后Django
调用views.month_archive(request,year=2005,month=3)
。 - 发送向
/articles/2003/
的请求将会与第一个URL
模式字符串匹配成功而不是第二个,因为Django
在第一个URL匹配成功后停止后续URL
检验,匹配成功后Django
调用views.special_case_2003(request)
。 - 发送向
/articles/2003
的请求不会与任何URL模式字符串匹配成功,因为每个URL模式字符串都要求以/结束。 - 发送向
/articles/2003/03/building-a-django-site/
的请求将会与最后一个URL
模式字符串匹配成功,匹配成功后Django
调用views.article_detail(request,year=2003,month=3,slug=”building-a-django-site”)
。
URL
参数类型转化器
前面提到可以使用int
对捕捉到的URL
参数进行类型转换,下面是Django
支持的所有类型转换器。
str
:匹配任意非空字符串,但是不能匹配URL
分隔符“/”。这是默认的URL参数转换器。int
:匹配任意大于等于0的整数。slug
:匹配任意slug
字符串,slug
字符串可以包含任意ASCII字符
、数字、连字符“-”和下画线”_”uuid
:匹配UUID
字符串(字符串中的字母必须为小写字-母),例如:075l94d3-6885-4l7e-a8a8-6c93le272tDO
。path
:匹配任意非空字符串,包括URL
分隔符“/”。这允许匹配完整的URL
而不是URL
的一个片段。
自定义URL参数类型转化器
对于更加复杂的URL场景,开发人员可以开发自定义参数类型转换器,自定义参数类型转换器包括以下几部分:
- 一个
regex
属性,属性值为正则表达式。 - 一个
to_python(self,value)
方法,该方法用于将匹配的URL
参数转换为指定类型,当类型转换失败后抛出ValueError
异常。 - 一个
to_url(self,value)
方法,该方法用于将Python
类型转换为类型转换器字符串。
下面是一个用于捕获日期年的类型转换器:
1 | class FourDigitYearConverter: |
使用register_converter()
将以上类型转换器注册到URLconf
。
1 | from django.urls import register_converter, path |
创建视图:
1 | def get_year(request, year): |
此时目录结构如下:
1 | polls/ |
启动Web 服务
访问URL:127.0.0.1:8000/polls/2017/即可看到页面输出:2017
使用正则表达式
与Django1.x
一样,Django2.0
仍然可以使用正则表达式匹配URL,此时需要使用re_path()
方法而不是path()
。Python
的正则表达式支持对分组进行命名,语法格式为:(?P<name>pattern)
,其中name
为分组名,pattern
为匹配的正则表达式。
使用正则表达式对前面的URLconf
进行重写效果如下:
1 | from django.urls import path, re_path |
虽然可以使用未命名的正则表达式,例如使用([0-9]{4})
替代(?P<year>[0-9]{4})
,但是为了防止出现意外错误,推荐对分组命名。
另外需要注意,不要将命名正则表达式与未命名正则表达式混合使用,这样会造成未命名正则表达式丢失。
最后正则表达式可以嵌套使用,如:
1 | re_path(r’comments/(?P<page-number>\d+)/)?$’, comments) |
导入其他URLconf
对于现代Web
应用程序来说,一个工程下通常会包含多个应用程序,每个应用程序包含很多URL
,如果将这些URL
都写在URLconf
根模块中,那么URLconf
将会变得非常脚肿,不利于维护。对于这种情况,常用的解决办法就是为每一个应用程序写一套独立的URLconf
,而URLconf
根模块通过使用include()
方法将其他URLconf
引用进来。
下面是Polls 网站的mysite/urls.py
:
1 | from django.contrib import admin |
当Django
遇到include()
方法时, URL
匹配工作跳转进入被引用的URLconf
进行验证。
使用include()
方法还可以引用其他URL
模式列表, 例如:
1 | from django.urls import include, path |
此时访问/credit/reports/
时将会调用credit_views.report()
视图方法。这样做的好处是当一个应用程序中多条URL
的前缀相同时,在本例中extra_pattems
中的URL 都是以credit
开头,可以简化URL
模式字符串。
向视图传递额外参数
可以使用path()
方法的第三个参数向视图传递额外参数, 例如:
1 | from django.urls import path |
也可以向include()
方法传递额外参数,例如:
1 | # main.py |
此时,额外参数{'blog_id': 3}
将会被传递给每一个被引用的URL
动态生成url
在网页应用中,很多情况下需要动态编写URL
,而不是用户直接在浏览器中输入URL
,例如网页超链接的URL
需要在生成网页时固定好。
用以下URL模式字符串为例,看看如何在Django
模板和视图中动态生成URL:
1 | path('articles/<int:year>/', views.year_archive, name='news-year-archive') |
使用
url
标签在模板中动态生成URL
1
2
3
4
5
6
7{# 使用固定值参数: #}
<a href="{% url 'new-year-archive' 2012 %}">2012 Archive</a>
<ul>
{% for yearvar in year_list %}
<li><a href = "{% url 'new-year-archive' yearvar %}">{{ yearvar }}Archive</a></li>
{% endfor %}
</ul>使用
reverse()
方法在python
代码中生成url
1
2
3
4
5
6from django.urls import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
year = 2016
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
URL
名字和命名空间
给URL
命名,可以方便地在模板或Python
代码中使用URL
,如前面示例中分别在模板和Python
代码中使用了URL
的名字'news-year-archive'
。URL
命名空间用于将URL
进行隔离。应用程序名就可以作用URL
的命名空间,例如django.contrib.admin
的命名空间就是admin
。由于Django
的应用程序可以部署多次,所以应用程序的实例名也可以作为命名空间。
使用”命名空间名:URL名
“的方式调用URL。命名空间可以嵌套使用如"命名空间名l:命名空间名2:URL名"
。
1 | from django.urls import path |
或者直接在urlpatterns
中定义命名空间:
1 | from django.urls import include, path |
上面polls_patterns
是一个元组,元组的第一个参数是path()
或re_path()
列表,第二个参数是URL的namespace
。当使用include()
方法引用polls_patterns
时系统会自动为polls_patterns
中的所有URL添加namespace
。
1 | from django.urls import include, path |
1 | {% url 'polls:index' %} |
1 | return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) |