django路由系统
django路由系统
为了替应用程序设计对应的urls, 我们创建了一个URLConf(url configuration)的模块. URL配置就像一个字典的目录一样, 让我们的应用程序(视图函数)和对应的url(本质是正则表达式)形成映射关系. 以这种方式, 用户访问哪个url, django就能到正确的映射函数里去执行了.
url基本配置
django 1.11版本的路由匹配
from django.conf.urls import url
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
-
正则表达式: 正则表达式模式串
-
views视图函数: 一个callable对象, 指向执行具体业务逻辑代码的函数
-
参数: 可选的要传递给视图函数的默认参数(字典形式)
-
一个可选的name参数, 反向解析时需要使用
下面是Django 1.11 文档中的典型例子.
from django.conf.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 这个字符串是“原始的” —— 字符串中任何字符都不应该转义
- urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续
django 2.0版本的路由配置
在Django2.0版本以上的路由配置与1.0+版本的路由配置有了一些变化.
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
注意事项:
- 使用尖括号(
<>
)从url中捕获值 - 捕获值中可以包含一个转化器类型(converter type),比如使用
<int:name>
捕获一个整数变量, 后面的视图函数的参数的名字也要与之对应. 若果没有转化器,将匹配任何字符串,当然也包括了/
字符. - 无需添加前导斜杠
- 默认的已经是使用path来做路由匹配了.
- 经过转换器后转换后的类型可能也已经变为相应的数据类型了
默认的path转换器
- str:非空的字符串类型。默认的转换器。但是不能包含斜杠;
- int:匹配任意的零或者正数的整形。到视图函数中就是一个int类型;
- slug:由英文中的横杠 - ,或者下划线 _ 连接英文字符或者数字而成的字符串;
- uuid:匹配 uuid 字符串;
- path:匹配非空的英文字符串,可以包含斜杠;
自定义转换器
对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:
-
regex
类属性,字符串类型 -
to_python(self, value)
方法,value是由类属性regex
所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。 -
to_url(self, value)
方法,和to_python
相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。
from django.urls import path, register_converter
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
"""直接转换, 传递给对应的视图函数中作为参数"""
return int(value)
def to_url(self, value):
return '%04d' % value
# 注册自定义的转换器
register_converter(FourDigitYearConverter, 'yyyy')
urlpatterns = [
path('books/<yyyy:year>/', views.book_year),
]
以上这些就是2.0以上版本的新的url匹配规则了, 下面的例子仍然以1.0版本的url(正则)来做路由匹配.
路由配置中的斜杠问题
我们自己写的路由函数中通常会在最后添加上/
后缀, 然后在settings全局配置中添加上下面一句话
APPEND_SLASH = True
这个配置项为true的含义是当用户输入的url不匹配任何一个url路由的时候, Django会先判断这个url是否是以/
结尾的,如果是, 则直接返回404错误, 如果不以/
结尾, 会自动加上/生成一个新的url, 然后再去urlConf进行匹配, 如果能访问的话, 便会返回一个301状态码, 并把这个新的url传给浏览器 , 如果还是不能访问, 则还会报404错误.
通常为了方便我们都会默认添加上这个斜杠.
分组
无名分组
下面的示例使用简单的正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式传递给视图。
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'books/(\d+)', views.books),
]
# views.py
def books(request, args):
# 在无名分组中, 括号捕获到的参数都会以位置参数的形式传递到视图函数中, 名字可以随意取
print(args)
return HttpResponse(f'无名分组, 接收到参数{args}')
有名分组
在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图.
在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式。
urlpatterns = [
url(r'books2/(?P<book_id>\d+)', views.books2),
]
def books2(request, book_id):
return HttpResponse(f'收到有名分组{book_id}')
有名分组能使表达式更加清晰, 并且捕获到的参数会以关键字参数的形式传递到视图函数中, 这意味着参数的顺序不那么重要, 也不容易出错.
路由分发
当应用有很多的时候, 且每一个应用都有上百个视图函数的时候, 这时如果都写在总路由里面, 那么显然不合理, 和拆分的模块的思路一样, 路由可以写在多个文件中, 然后使用include函数导入即可.
urls = [
url(r'^test1/', views.test1),
]
urlpatterns = [
url(r'test/', include(urls)),
]
def test1(request):
return HttpResponse('路由分发的test1')
这时候再浏览器里敲地址test/开头的都会被分发到urls这个子路由里面, 进行匹配, 和之前的逻辑并无不同.
命名URL和反向解析
在使用Django 项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。
人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。
换句话讲,需要的是一个DRY 机制。除了其它有点,它还允许设计的URL 可以自动更新而不用遍历项目的源代码来搜索并替换过期的URL。
获取一个URL 最开始想到的信息是处理它视图的标识(例如名字),查找正确的URL 的其它必要的信息有视图参数的类型(位置参数、关键字参数)和值。
Django 提供一个办法是让URL 映射是URL 设计唯一的地方。你填充你的URLconf,然后可以双向使用它:
- 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
- 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。
第一种方式是我们在前面的章节中一直讨论的用法。第二种方式叫做反向解析URL、反向URL 匹配、反向URL 查询或者简单的URL 反查。
在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:
- 在模板中:使用url模板标签。
- 在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
以上是官方文档的直白翻译, 用大白话讲就是我们不希望在一些需要使用到url的地方把这个url的地址写死, 后期我们可能由于升级等原因需要改动url地址, 那么原来写死的url就可能过期无效, 这时候维护更新就非常麻烦, 所以需要找到一种反映映射关系的技术. 而这种方式就是Django给我们提供的url命名与url反向解析.
在上面我们也提到了url的基本配置还可以填写一个name参数, 这个就是当前url的名字. 使用方式如下所示:
urlpatterns = [
url(r'books/(?P<book_id>\d+)', views.books, name='book_detail'),
]
from django.urls import reverse
def books(request, book_id):
print(reverse('book_detail', kwargs={'book_id': 5})) # out: /books/5
return HttpResponse('Book')
# 模板中的写法
{% url 'book_detail' book_id=6 %}
正确的反向解析需要正确的传递给路由需要的参数, 如果是无名分组, 需要传递给args参数, 如果是有名分组, 需要将参数传递给kwargs字典中.
名称空间
真实的项目中会存在多个app, 而这些app又分别由许多人共同开发, 这就涉及到可能多个url名称会相同, 这时候想要正确的反向解析到对应app里的视图函数, 就需要利用到名称空间了.
具体用法见下面:
# 总路由, 进行分发, 配置各自的名称空间
urlpatterns = [
url(r'app01/', include('app01.urls', namespace='app01')),
url(r'app02/', include('app02.urls', namespace='app02')),
]
# app01的路由和视图
urlpatterns = [
url(r'books/(?P<book_id>\d+)', views.books, name='book_detail'),
url(r'test/', views.test),
]
# 视图
def books(request, book_id):
print(reverse('book_detail', kwargs={'book_id': 5}))
return HttpResponse('Book')
def test(request):
print(reverse('app01:book_detail', kwargs={'book_id': 5}))
print(reverse('app02:book_detail', kwargs={'book_id': 7}))
return HttpResponse('ok')
# app02的路由和视图
urlpatterns = [
url(r'books/(?P<book_id>\d+)', views.books, name='book_detail'),
]
# 现在在浏览器敲入http://127.0.0.1:8000/app01/test/进行测试, 最后test函数可以正确的输出
# 结果也如预期正确解析出来
# /app01/books/5
# /app02/books/7
模板中的使用和反向解析一样, 只需要加上名称空间名字: url名字
即可.
{% url 'app01:book_detail' book_id=7 %}