codecamp

Django4.0 URL调度器-URL命名空间

介绍

URL 命名空间允许你使用唯一的反向命名URL模式( named URL patterns ),即便不同应用程序使用相同的 URL 名称。对于第三方应用程序来说,始终使用 URL 命名空间是个好习惯(像在教程里说的那样)。同样,如果已部署了应用程序的多个实例,它也允许你反向解析 URL。换句话说,因为单个应用的多个实例会分享已命名的 URL,命名空间提供了区分这些已命名 URL 的方法。

对于特定站点,正确使用URL名称空间的Django应用程序可以部署多次。比如 ​django.contrib.admin​ 有一个 ​AdminSite ​,它允许admin实例部署多次。

URL 命名空间分为两部分,它们都是字符串:

应用程序命名空间

这描述了正在部署的程序名。单个应用的每个实例拥有相同的命名空间。比如,Django admin 应用有可预测的应用命名空间 ​'admin'​ 。

实例命名空间

这标识了应用程序的特定实例。实例命名空间应该是完整项目唯一的。但是实例命名空间可以和应用命名空间相同。这常用来指定应用的默认实例。比如,默认Django admin 实例拥有名为​'admin'​ 的实例命名空间。

被指定的命名空间 URL 使用 ​':'​ 操作符。比如,使用 ​'admin:index'​ 引用admin 应用的首页。这表明命名空间为 ​'admin'​ ,命名 URL 为 ​'index'​ 。

命名空间也可以嵌套。命名 URL ​'sports:polls:index'​ 将在命名空间 ​'polls'​ 中寻找命名为 ​'index'​ 的模式,该模式是在顶层命名空间 ​'sports'​ 中定义的。

反向命名空间URLs

当给定一个命名空间 URL(例如 ​'polls:index'​ )解析时,Django 会将完全限定的名称拆分成多个部分,然后尝试下面的查询:

  • 首先,Django 查找匹配 ​application namespace​ (这个例子里是 ​'polls'​ )。这将产生应用实例列表。
  • 如果定义了当前应用程序,Django 会为这个实例查找并返回 URL 解析器。可以用 ​reverse()​ 函数的 ​current_app参数来指定当前应用程序。url 模板标签使用当前已解析的视图命名空间当作 ​RequestContext中的应用程序。你可以通过设置在 ​request.current_app​ 属性上的当前应用来覆盖这个默认配置。
  • 如果当前没有应用程序,Django 会寻找默认的应用实例。默认应用程序实例是具有与实例命名空间匹配的应用程序命名空间的实例(比如, ​polls ​实例被称为 ​'polls'​ )。
  • 如果没有默认的应用程序实例,Django 将会引用最后一次部署的应用程序实例,无论其实例命名是什么。
  • 如果提供的命名空间无法在第一步里匹配应用程序命名空间,Django 会尝试直接寻找命名空间来作为实例命名空间。

如果有嵌套的命名空间,则会对命名空间的每个部分重复这些步骤,直到视图名不被解析为止,然后视图名称将被解析为已找到的命名空间中的一个 URL 。

例如:

为了展示这个解决策略的实际作用,请考虑教程里 ​polls应用程序的两个实例案例:分别被称为 ​'author-polls'​ 和 ​'publisher-polls'​ 。假设我们已经增强了这个应用程序,以便会在创建和显示 ​polls时考虑实例命名空间

from django.urls import include, path

urlpatterns = [
    path('author-polls/', include('polls.urls', namespace='author-polls')),
    path('publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    ...
]

使用这步后,可以进行以下查找:

如果我们在实例 ​'author-polls'​ 中渲染详情页 - ​'polls:index'​ 将解析为 ​'author-polls'​ 的首页;比如下面两种都将触发 ​"/author-polls/"​ 。

在基于类的视图里的方法:

reverse('polls:index', current_app=self.request.resolver_match.namespace)

在模板中:

{% url 'polls:index' %}

如果我们在站点某处渲染一个页面 - ​'polls:index'​ 将被解析为 ​polls的最后一个注册实例。因为这里没有默认实例( ​'polls'​ 的实例命名空间),所以将使用 ​polls的最后一个注册实例。这将是 ​'publisher-polls'​ ,因为它是在 ​urlpatterns​ 的最后面声明的。

'author-polls:index'​ 会一直被解析为实例 ​'author-polls'​ 的首页(对于 ​'publisher-polls'​ 同样如此)。

如果还有一个默认实例 - 例如,一个叫 ​'polls'​ 的实例 - 唯一的变化就是没有当前实例(上面列表中的第二项)。在这个例子 ​'polls:index'​ 将解析为默认实例的首页而不是在 ​urlpatterns中最后声明的实例。

URL 命名空间和包含的 URLconfs

有两种办法指定包含的URLconfs应用程序空间。

首先,你可以在包含的 URLcon 模块中设置一个 ​app_name​ 属性,在相同层作为 ​urlpatterns属性。你必须传递实际的模块或对该模块的一个字符串引用传递给 ​include()​ ,而不是 ​urlpatterns ​本身的列表。

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    ...
]
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
]

polls.urls​ 里的 URLs 定义将具有应用程序命名空间 ​polls

其次,你可以包括一个包含嵌入式命名空间数据的对象。如果你 ​include()​ 了一个 ​path()​ 或 ​re_path()​ 实例的列表,那个对象里包含的 URLs 将被添加到全局命名空间内。但是,你也可以 ​include()​ 一个包含以下内容的2元组:

(<list of path()/re_path() instances>, <application namespace>)

例如:

from django.urls import include, path

from . import views

polls_patterns = ([
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
], 'polls')

urlpatterns = [
    path('polls/', include(polls_patterns)),
]

这会将指定的 URL 模式包含到给定的应用程序命名空间里。

使用 ​include()​ 的 ​namespace ​参数来指定实例命名空间。如果实例命名空间没有被指定,会默认已被导入的 URLconf 的应用程序命名空间。这意味着它也将成为那个命名空间的默认实例。


Django4.0 URL调度器-命名URL模式
Django4.0 编写视图-一个简单的视图
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

Django4.0 模型和数据库

Django4.0 处理HTTP请求

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }