Django路由系统
-
URL调度器
- Django 允许自由地设计你的URL,不受框架束缚。Django认为,对于高质量的Web 应用来说,使用简洁、优雅的URL 模式是一个非常值得重视的细节。
- 为了给一个应用设计URL,你需要创建一个Python 模块,通常被称为URLconf(URL configuration)。URLconf也被称作路由系统,这个模块是纯粹的Python 代码,包含URL 模式(简单的正则表达式)到Python 函数(你的视图)的简单映射。可以把URLconf比作书的目录。
-
URLconf的基本格式
-
在Django项目名称目录(以项目名称为:"Django" 为例)的
urls.py
文件中,找到urlpatterns
from django.urls import path from app import views # 从app中导入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 中取值,使用尖括号。
- 捕获的值可以选择性地包含转换器类型。比如,使用
<int:name>
来捕获整型参数。如果不包含转换器,则会匹配除了/
外的任何字符。 - 路径的最前面不需要添加反斜杠,因为每个 URL 都有。比如,应该是 articles 而不是 /articles 。
-
-
URLconf的路径转换器
下面的路径转换器在默认情况下是有效的:
-
str
- 匹配除了'/'
之外的非空字符串。如果表达式内不包含转换器,则会默认匹配字符串。 -
int
- 匹配0或任何正整数。返回一个 int 。 -
slug
- 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。比如,building-your-1st-django-site
。 -
uuid
- 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00
。返回一个UUID
实例。 -
path
- 匹配非空字段,包括路径分隔符'/'
。它允许你匹配完整的 URL 路径而不是像str
那样只匹配 URL 的一部分。
-
-
URLconf注册自定义的路径转换器
对于更复杂的匹配需求,可以定义自己的路径转换器。
转换器是一个类,包含如下内容:
字符串形式的
regex
类属性。to_python(self, value)
方法,它处理匹配的字符串转换为应该传递到函数的类型。如果没有转换为给定的值,它应该会引发ValueError
。ValueError
被解释为不匹配,因此向用户发送404响应。-
to_url(self, value)
,处理将 Python 类型转换为 URL 中要使用的字符串。class FourDigitYearConverter: regex = '[0-9]{4}' def to_python(self, value): return int(value) def to_url(self, value): return '%04d' % value
-
在 URLconf 中使用
register_converter()
来注册自定义的转换器类:from django.urls import path, register_converter from app import converters, views register_converter(converters.FourDigitYearConverter, 'yyyy') urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<yyyy:year>/', views.year_archive), ... ]
-
URLconf使用正则表达式
如果路径和转化器语法不能很好的定义你的 URL 模式,你可以可以使用正则表达式。如果要这样做,请使用
re_path()
而不是path()
。在 Python 正则表达式中,命名正则表达式组的语法是
(?P<name>pattern)
,其中name
是组名,pattern
是要匹配的模式。这里是先前 URLconf 的一些例子,现在用正则表达式重写一下:
from django.urls import path, re_path from app import views urlpatterns = [ path('articles/2003/', views.special_case_2003), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail), ]
这实现了与前面示例大致相同的功能,除了:
- 将要匹配的 URLs 将稍受限制。比如,10000 年将不在匹配,因为 year 被限制长度为4。
- 无论正则表达式进行哪种匹配,每个捕获的参数都作为字符串发送到视图。
当从使用
path()
切换到re_path()
(反之亦然),要特别注意,视图参数类型可能发生变化,你可能需要调整你的视图。
-
URLconf使用未命名的正则表达式组
还有命名组语法,例如
(?P<year>[0-9]{4})
,你也可以使用更短的未命名组,例如([0-9]{4})
。不是特别推荐这个用法,因为它会更容易在匹配的预期含义和视图参数之间引发错误。
在任何情况下,推荐在给定的正则表达式里只使用一个样式。当混杂两种样式时,任何未命名的组都会被忽略,而且只有命名的组才会传递给视图函数。
-
URLconf中包含其他的URLconfs
-
在任何时候,你的
urlpatterns
都可以 "include" 其它URLconf 模块。这实际上将一部分URL 放置于其它URL 下面。例如,下面是URLconf Django website 自己的URLconf 中一个片段。它包含许多其它URLconf:from django.urls import include, path urlpatterns = [ # ... snip ... path('community/', include('aggregator.urls')), path('contact/', include('contact.urls')), # ... snip ... ]
-
每当 Django 遇到
include()
,它会将匹配到该点的URLconf的任何部分切掉,并将剩余的字符串发送到包含的URLconf进行进一步处理。另一种可能性是通过使用path()
实例的列表来包含其他 URL 模式。比如,看这个 URLconf:from django.urls import include, path from apps.main import views as main_views from credit import views as credit_views extra_patterns = [ path('reports/', credit_views.report), path('reports/<int:id>/', credit_views.report), path('charge/', credit_views.charge), ] urlpatterns = [ path('', main_views.homepage), path('help/', include('apps.help.urls')), path('credit/', include(extra_patterns)), ]
-
在这个例子中,
/credit/reports/
URL将被credit.views.report()
这个Django 视图处理。这种方法可以用来去除URLconf 中的冗余,其中某个模式前缀被重复使用。例如,考虑这个URLconf:from django.urls import path from . import views urlpatterns = [ path('<page_slug>-<page_id>/history/', views.history), path('<page_slug>-<page_id>/edit/', views.edit), path('<page_slug>-<page_id>/discuss/', views.discuss), path('<page_slug>-<page_id>/permissions/', views.permissions), ]
-
我们可以改进它,通过只声明共同的路径前缀一次并将后面的部分分组:
from django.urls import include, path from . import views urlpatterns = [ path('<page_slug>-<page_id>/', include([ path('history/', views.history), path('edit/', views.edit), path('discuss/', views.discuss), path('permissions/', views.permissions), ])), ]
-
-
URL的命名和URL的反向解析
-
URLconf中支持给URL匹配规则和URL匹配模式起一个名字,这样我们以后就不需要写死URL代码了,只需要通过名字来调用当前的URL。例如,我们在
urls.py
中:from django.urls import path from app import views urlpatterns = [ #... path('articles/<int:year>/', views.year_archive, name='news-year-archive'), #... ]
-
在模板里面可以这样引用:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a> {# Or with the year in a template context variable: #} <ul> {% for yearvar in year_list %} <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul>
-
在views函数中可以这样引用:
from django.http import HttpResponseRedirect from django.urls import reverse def redirect_to_year(request): # ... year = 2006 # ... return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
-