Django之路由系统
Django之路由系统
URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。也就是以这种方式告诉Django,对于指定的URL调用指定的代码函数。
URLConf配置
Django如何处理一个请求?
当用户从你的django驱动的站点请求一个页面时,系统按照以下算法来确定需要执行的Python代码函数:
- Django确定要使用的根URLconf模块。通常,这是ROOT_URLCONF设置的值,但是如果传入的HttpRequest对象具有urlconf属性(由中间件设置),则将使用它的值来代替ROOT_URLCONF设置。
- Django加载Python模块并查找变量urlpatterns。这是一个django.url .path()和/或django.url .re_path()实例的序列。
- Django按顺序遍历每个URL模式,并在第一个匹配请求的URL的模式处停止。
- 一旦其中一个URL模式匹配,Django就会导入并调用给定的视图,该视图是一个简单的Python函数(或基于类的视图)。视图通过以下参数传递:
- HttpRequest的一个实例。
- 如果匹配的URL模式没有返回指定的组,那么正则表达式中的匹配将作为位置参数提供。
- 关键字参数由路径表达式匹配的任何命名部分组成,由可选的kwargs参数(django.url .path()或django.url .re_path()中指定的任何参数覆盖。
5. 如果没有匹配的URL模式,或者在此过程的任何时间点引发异常,Django将调用适当的错误处理视图。参见下面的错误处理。
一个简单的例子:
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), ]
注意:1.匹配除/字符之外的任何字符串。2.不需要添加前导斜杠,因为每个URL都有。例如,它是articles,而不是/articles。
下列路径转换器在预设情况下是可用的:
str—匹配任何非空字符串,不包括路径分隔符'/'。如果表达式中没有包含转换器,这是默认的。 int—匹配0或任何正整数。返回一个int。 slug-匹配任何由ASCII字符或数字,加上连字符和下划线字符组成的slug字符串。例如,building-your-1st-django-site。 uuid -匹配格式化的uuid。为了防止多个url映射到同一个页面,必须包含破折号和小写字母。例如,075194 d3 - 6885 - 417 - e - a8a8 c931e272f00——6。返回一个UUID实例。 path—匹配任何非空字符串,包括路径分隔符'/'。这允许您匹配一个完整的URL路径,而不是像str那样只匹配URL路径的一部分。
使用正则表达式
如果路径和转换器语法不足以定义URL模式,还可以使用正则表达式。为此,使用re_path()而不是path()。在Python正则表达式中,命名正则表达式组的语法是(?P<name>pattern)
,其中name是组的名称,pattern是要匹配的一些模式。下面是之前使用正则表达式重写的URLconf示例:
from django.urls import path, re_path from . 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), ]
这实现了与前一个例子大致相同的事情,除了:
- 将匹配的确切url稍微受到一些限制。例如,10000年将不再匹配,因为年份整数被限制为正好四位数字长。
- 每个捕获的参数都以字符串的形式发送到视图,而不管正则表达式匹配什么类型
在从使用path()切换到re_path()或从使用re_path()切换到使用path()时,注意视图参数的类型可能会改变,因此可能需要调整视图。
当命名正则和非命名正则两种样式混合时,任何未命名的组都被忽略,只有命名的组被传递给视图函数。
注意:
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项 APPEND_SLASH=True
Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。
其他URLconfs
在任何时候,您的urlpatterns都可以“包括”其他URLconf模块。这本质上是一组位于其他url之下的“根”。例如,下面是Django网站本身的URLconf的摘录。它包括一些其他的URLconfs:
from django.urls import include, path urlpatterns = [ # ... snip ... path('community/', include('aggregator.urls')), path('contact/', include('contact.urls')), # ... snip ... ]
通过使用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视图处理。这可以用于从重复使用单个模式前缀的URLconfs中删除冗余。例如,考虑这个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), ])), ]
包含的URLconf从父URLconfs接收任何捕获的参数:
# In settings/urls/main.py from django.urls import include, path urlpatterns = [ path('<username>/blog/', include('foo.urls.blog')), ] # In foo/urls/blog.py from django.urls import path from . import views urlpatterns = [ path('', views.blog.index), path('archive/', views.blog.archive), ]
在上面的例子中,捕获的“username”变量按照预期传递到包含的URLconf。
命名URL和URL反向解析
Django提供了执行URL反转的工具,以匹配需要URL的不同层:
- 在模板中:使用url模板标记。
- 在Python代码中:使用reverse()函数。
- 在与处理Django模型实例的url相关的高级代码中:get_absolute_url()方法。
再次考虑URLconf条目:
from django.urls import path from . import views urlpatterns = [ #... path('articles/<int:year>/', views.year_archive, name='news-year-archive'), #... ]
与年份nnnn对应的归档URL为/articles//。你可以在模板代码中使用:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a> {# 或者将年份放在模板上下文变量中: #} <ul> {% for yearvar in year_list %} <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul>
或者在python中的代码:
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,)))
注意:在一些视图具有一般性质的场景中,url和视图之间可能存在多对一关系。在这些情况下,视图名在反转url时不是一个足够好的标识符。
命名的URL模式
为了执行URL反转,需要使用上面示例中所使用的命名URL模式。用于URL名称的字符串可以包含自己喜欢的任何字符。不受限于有效的Python名称。在命名URL模式时,选择不太可能与其他应用程序的名称选择冲突的名称。如果你调用你的URL模式注释,而另一个应用程序执行相同的操作,那么reverse()找到的URL取决于您的项目的urlpatterns列表中的最后一个模式。在URL名称上加上前缀(可能来自应用程序名称(比如myapp-comment而不是comment))可以减少冲突的几率。如果要覆盖视图,可以故意选择与另一个应用程序相同的URL名称。例如,一个常见的用例是覆盖LoginView。Django的某些部分和大多数第三方应用程序都假设这个视图有一个名为login的URL模式。如果您有一个自定义登录视图,并将其URL命名为login, reverse()将找到您的自定义视图,只要它是在django.contrib.auth之后的urlpatterns中。包括url(如果有的话)。如果多个URL模式的参数不同,也可以使用相同的名称。除了URL名称之外,reverse()还匹配参数的数量和关键字参数的名称。
URL命名空间
URL名称空间允许您唯一地反向命名URL模式,即使不同的应用程序使用相同的URL名称。
举个例子:
project中的urls.py
from django.conf.urls import url, include urlpatterns = [ path('app01/', include('app01.urls', namespace='app01')), path('app02/', include('app02.urls', namespace='app02')), ]
from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ path('(?P<pk>\d+)/$', views.detail, name='detail') ]
from django.conf.urls import url from app02 import views app_name = 'app02' urlpatterns = [ path('^(?P<pk>\d+)/$', views.detail, name='detail') ]
现在,我的两个app中 url名称重复了,我反转URL的时候就可以通过命名空间的名称得到我当前的URL。
语法:
'命名空间名称:URL名称'
模板中使用:
{% url 'app01:detail' pk=12 pp=99 %}
views中的函数中使用
v = reverse('app01:detail', kwargs={'pk':11})
这样即使app中URL的命名相同,我也可以反转得到正确的URL了。