结合遍历和URL调度¶
当你写得最多的时候 Pyramid 应用程序,您将使用两个可用的应用程序中的一个 resource location 子系统:遍历或URL调度。但是,为了解决一组有限的问题,使用 both 遍历和URL在同一应用程序中一起调度。 Pyramid 通过 混合的 应用。
警告
对于“混合”的URL调度+遍历应用程序的行为进行推理可能具有挑战性。要成功地将URL调度和遍历一起使用,您需要了解URL模式匹配、根工厂和 traversal 算法,以及它们之间潜在的交互作用。因此,我们不建议创建依赖混合行为的应用程序,除非必须这样做。
非混合应用综述¶
根据其文档中的教程使用时, Pyramid 是一个“双模式”框架:教程解释了如何使用 URL dispatch or traversal . 本章详细介绍了如何组合这两种调度机制,但在尝试组合它们之前,我们将回顾它们是如何独立工作的。
仅限URL调度¶
使用 URL dispatch 专门用于将URL映射到代码的语句通常在其应用程序启动配置中包含如下语句:
1# config is an instance of pyramid.config.Configurator
2
3config.add_route('foobar', '{foo}/{bar}')
4config.add_route('bazbuz', '{baz}/{buz}')
5
6config.add_view('myproject.views.foobar', route_name='foobar')
7config.add_view('myproject.views.bazbuz', route_name='bazbuz')
各 route 对应于一个或多个视图可调用文件。通过传递 route_name
调用期间与其名称匹配的参数 add_view()
. 在请求期间匹配路由时, view lookup 用于将请求与其关联的可调用视图匹配。有人打电话给 add_route()
表示应用程序正在使用URL调度。
仅遍历¶
仅使用遍历的应用程序将具有如下所示的视图配置声明:
1# config is an instance of pyramid.config.Configurator
2
3config.add_view('mypackage.views.foobar', name='foobar')
4config.add_view('mypackage.views.bazbuz', name='bazbuz')
当将上述配置应用于应用程序时, mypackage.views.foobar
当URL /foobar
参观。同样,这个观点 mypackage.views.bazbuz
当URL /bazbuz
参观。
通常,专门使用遍历的应用程序不会执行对 pyramid.config.Configurator.add_route()
在它的启动代码中。
混合应用¶
可以单独使用遍历或URL调度来创建 Pyramid 应用。然而,在构建应用程序时,也可以将遍历和URL调度的概念结合起来,其结果是混合应用程序。在混合应用程序中,执行遍历 之后 一条特定的路线已经匹配。
混合应用程序更像是基于“纯”遍历的应用程序,而不是基于“纯”URL调度的应用程序。但与基于“纯”遍历的应用程序不同,在混合应用程序中 traversal 在路由匹配后的请求期间执行。这意味着表示 pattern
路由的参数必须与 PATH_INFO
在路由模式匹配之后,大多数关于 resource location 和 view lookup 申请。
纯遍历应用程序和混合应用程序之间只有四个真正的区别:
在纯基于遍历的应用程序中,没有定义路由。在混合应用程序中,将至少定义一个路由。
在纯基于遍历的应用程序中,使用的根对象是全局的,由 root factory 在启动时提供。在混合应用程序中, root 开始遍历的对象可以根据每个路由进行更改。
在纯基于遍历的应用程序中,
PATH_INFO
潜在的 WSGI 环境被批量用作遍历路径。在混合应用程序中,遍历路径不是完整的PATH_INFO
字符串,但由匹配路由配置模式中的匹配模式确定的URL的一部分。在纯基于遍历的应用程序中,查看不提到
route_name
在 view lookup . 在混合应用程序中,当路由匹配时,只查看将该路由的名称作为route_name
在 view lookup .
一般来说,混合应用程序 is 基于遍历的应用程序,除了:
遍历 root 根据匹配的路由的路由配置选择,而不是从
root_factory
在应用程序启动配置期间提供。遍历 path 是根据匹配的路由的路由配置选择的,而不是从
PATH_INFO
请求的在 view lookup 当路由匹配仅限于特定名称的路由时
route_name
在它们的配置中,与匹配的路由相同name
.
要创建混合模式应用程序,请使用 route configuration 这意味着 root factory 其中还包括 pattern
包含特殊动态部分的参数:或者 *traverse
或 *subpath
.
路由匹配的根对象¶
混合应用程序意味着在路由匹配之后的请求期间执行遍历。根据定义,遍历必须始终从根对象开始。因此,了解 哪一个 路由匹配后将遍历根对象。
找出 root 特定路由匹配的对象结果很简单。匹配路由时:
如果路由的配置具有
factory
指向a的参数 root factory Callable,将调用该Callable以生成 root 对象。如果路由的配置没有
factory
论点 全球的 root factory 将被调用以生成 root 对象。全局根工厂是root_factory
传递给的参数Configurator
在应用程序启动时。如果A
root_factory
参数未提供给Configurator
启动时,A 违约 使用根工厂。默认根工厂用于生成根对象。
使用 *traverse
以路线模式¶
混合应用程序通常意味着包含特殊令牌的路由配置 *traverse
在路线模式的末端:
1config.add_route('home', '{foo}/{bar}/*traverse')
A *traverse
路由配置中模式末尾的标记表示“余数” 捕获 价值。当它被使用时,它将匹配URL的其余路径段。其余部分将成为用于执行遍历的路径。
备注
这个 *remainder
路由模式语法在 路由模式语法 .
混合模式应用程序更依赖于 traversal 做 resource location 和 view lookup 比大多数例子所表明的 URL调度 .
因为上述路线的模式以 *traverse
,在请求期间匹配此路由配置时, Pyramid 将尝试使用 traversal 反对 root 对象所隐含的 root factory 这是路由配置所暗示的。因为没有 root_factory
为该路由显式指定了参数,这将是 全球的 应用程序的根工厂,或 违约 根工厂。一次 traversal 找到了一个 context 资源, view lookup 将以与在基于“纯”遍历的应用程序中调用几乎完全相同的方式进行调用。
假设没有 全球的 root factory 在此应用程序中配置。这个 违约 root factory 无法遍历;它没有用处 __getitem__
方法。因此,我们需要将此路由配置与自定义根工厂相关联,以便创建一个有用的混合应用程序。为此,假设我们在一个名为 routes.py
:
1class Resource(object):
2 def __init__(self, subobjects):
3 self.subobjects = subobjects
4
5 def __getitem__(self, name):
6 return self.subobjects[name]
7
8root = Resource(
9 {'a': Resource({'b': Resource({'c': Resource({})})})}
10 )
11
12def root_factory(request):
13 return root
上面我们定义了一个可以遍历的(伪)资源树,以及 root_factory
可作为特定路由配置语句的一部分使用的函数:
1config.add_route('home', '{foo}/{bar}/*traverse',
2 factory='mypackage.routes.root_factory')
这个 factory
以上是我们定义的函数。它将返回 Resource
每当此路由匹配时,将类作为根对象。的实例 Resource
类可用于树遍历,因为它们具有 __getitem__
做一些名义上有用的事情的方法。因为遍历使用 __getitem__
要遍历资源树的资源,对路由语句所隐含的根资源进行遍历是一件合理的事情。
备注
我们也可以用我们的 root_factory
作为 root_factory
论证 Configurator
而不是将其与路由配置中的特定路由关联。所有匹配的混合路由配置,但是 not 名字A factory
属性,将使用全局 root_factory
函数生成根对象。
当路由配置命名为 home
上面是在请求期间匹配的,生成的matchdict将基于其模式: {{foo}}/{{bar}}/*traverse
. “捕获值”由 *traverse
模式中的元素将用于遍历资源树,以便从根工厂返回的根对象开始查找上下文资源。在上面的示例中, root 找到的对象将是名为 root
在里面 routes.py
.
如果与模式匹配的路由的URL {{foo}}/{{bar}}/*traverse
是 http://example.com/one/two/a/b/c
,用于根对象的遍历路径将 a/b/c
. 因此, Pyramid 将尝试穿过边缘 'a'
, 'b'
和 'c'
,从根对象开始。
在上面的示例中,这组特定的遍历步骤意味着 context 视图的资源将是 Resource
我们命名的对象 'c'
在我们的伪资源树中, view name 遍历的结果将是空字符串。如果你需要一个关于为什么假设这个结果的更新,请参见 遍历算法 .
此时,将找到合适的可调用视图,并使用 view lookup 如上所述 查看配置 ,但需要注意:为了使视图查找工作,我们需要定义一个与 view lookup 在路由匹配后调用:
1config.add_route('home', '{foo}/{bar}/*traverse',
2 factory='mypackage.routes.root_factory')
3config.add_view('mypackage.views.myview', route_name='home')
请注意,上述调用 add_view()
包括一个 route_name
参数。查看包含 route_name
参数的作用是使用路由的名称将特定的视图声明与路由关联,以指示该视图应该 仅在路由匹配时调用 .
呼叫 add_view()
可以通过 route_name
属性,它引用现有路由的值 name
参数。在上面的示例中,路由名称是 home
,引用上面定义的路由的名称。
以上 mypackage.views.myview
当满足以下条件时,将调用View Callable:
当混合路由匹配时,也可以声明可调用的可选视图:
1config.add_route('home', '{foo}/{bar}/*traverse',
2 factory='mypackage.routes.root_factory')
3config.add_view('mypackage.views.myview', route_name='home')
4config.add_view('mypackage.views.another_view', route_name='home',
5 name='another')
这个 add_view
需要 mypackage.views.another_view
上面提到了不同的观点,更重要的是,不同的观点 view name . 以上 mypackage.views.another_view
满足以下条件时将调用视图:
例如,如果URL http://example.com/one/two/a/another
提供给使用前面提到的资源树的应用程序, mypackage.views.another_view
将调用View Callable而不是 mypackage.views.myview
查看Callable,因为 view name 将 another
而不是空字符串。
可以组成更复杂的匹配。所有参数 路线 配置语句和 view 在混合应用程序中支持配置语句(例如 predicate 参数)。
使用 traverse
路由定义中的参数¶
而不是使用 *traverse
余数标记在模式中,可以使用 traverse
论据 add_route()
方法。
当你使用 *traverse
剩余标记,当路由匹配时,遍历路径被限制为请求URL的剩余段。但是,当您使用 traverse
参数或属性,您可以更好地控制如何组成遍历路径。
这里有一个使用 traverse
调用中的模式 add_route()
:
1config.add_route('abc', '/articles/{article}/edit',
2 traverse='/{article}')
的语法 traverse
论点与之相同 pattern
.
如果,如上所述, pattern
提供的是 /articles/{{article}}/edit
和 traverse
提供的参数为 /{{article}}
,当一个请求出现,导致路由匹配时, article
匹配值为 1
(当请求URI为 /articles/1/edit
)遍历路径将生成为 /1
. 这意味着根对象 __getitem__
将用名称调用 1
在穿越阶段。如果 1
对象存在,它将成为 context 请求。这个 遍历 第章有更多关于遍历的信息。
如果遍历路径包含模式参数中不存在的段标记名,则会发生运行时错误。这个 traverse
模式不应包含不存在于 path
.
请注意 traverse
当参数附加到具有 *traverse
图案中的余数标记。
遍历将从该路由隐含的根对象(全局根或由返回的对象)开始 factory
与此路由关联)。
使全局视图匹配¶
默认情况下,仅查看提到 route_name
在视图查找过程中,当具有 *traverse
在它的模式匹配。您可以不使用 route_name
属性以通过添加 use_global_views
标记为路由定义。例如, myproject.views.bazbuz
如果路由名为 abc
下面是匹配的 PATH_INFO
是 /abc/bazbuz
,即使视图配置语句没有 route_name="abc"
属性。
1config.add_route('abc', '/abc/*traverse', use_global_views=True)
2config.add_view('myproject.views.bazbuz', name='bazbuz')
使用 *subpath
以路线模式¶
有一些非常罕见的情况,当你想影响遍历 subpath 当路由匹配而没有实际执行遍历时。例如, pyramid.wsgi.wsgiapp2()
装饰师和 pyramid.static.static_view
帮助程序尝试计算 PATH_INFO
从请求的子路径 use_subpath
论证是 True
,所以能够影响这个值很有用。
什么时候? *subpath
存在于模式中,没有实际遍历路径,但遍历算法将返回 subpath 捕获值所隐含的列表 *subpath
. 您将在如下所示的路由声明中看到这种模式:
1from pyramid.static import static_view
2
3www = static_view('mypackage:static', use_subpath=True)
4
5config.add_route('static', '/static/*subpath')
6config.add_view(www, route_name='static')
mypackage.views.www
是的实例 pyramid.static.static_view
. 这有效地告诉静态助手以文件名的形式遍历子路径中的所有内容。
生成混合URL¶
在 1.5 版本加入.
这个 pyramid.request.Request.resource_url()
方法与 pyramid.request.Request.resource_path()
方法都接受可选的关键字参数,使生成包含遍历资源路径的路由前缀URL更容易: route_name
, route_kw
和 route_remainder_name
.
具有包含 *remainder
模式(任何Stararg余数模式,如 *traverse
, *subpath
或 *fred
)可以用作的目标名称 request.resource_url(..., route_name=)
和 request.resource_path(..., route_name=)
.
例如,假设您在金字塔应用程序中定义了这样一个路由:
config.add_route('mysection', '/mysection*traverse')
如果要生成URL http://example.com/mysection/a/
,您可以使用下面的咒语,假设变量 a
下面指向的资源是根目录的子目录 __name__
属于 a
:
request.resource_url(a, route_name='mysection')
只能生成路径部分 /mysection/a/
假设相同:
request.resource_path(a, route_name='mysection')
路径是虚拟主机感知的,因此如果 X-Vhm-Root
环境变量存在于请求中,并设置为 /a
,上述要求 request.resource_url
将生成 http://example.com/mysection/
以及上述要求 request.resource_path
将生成 /mysection/
. 见 虚拟根支持 更多信息。
如果您尝试使用的路由需要填写简单的动态部分值才能成功生成URL,则可以将这些值作为 route_kw
参数 resource_url
和 resource_path
. 例如,假设路由定义是这样的:
config.add_route('mysection', '/{id}/mysection*traverse')
你可以通过 route_kw
在填写 {{id}}
以上:
request.resource_url(a, route_name='mysection', route_kw={'id':'1'})
如果你通过 route_kw
但不通过 route_name
, route_kw
将被忽略。
默认情况下,此功能通过调用 route_url
在引擎盖下,并将资源路径的值传递给该函数 traverse
. 如果你的路线不同 *stararg
余数名称(例如 *subpath
你可以告诉 resource_url
或 resource_path
用它代替 traverse
旁路 route_remainder_name
. 例如,如果您有以下路由:
config.add_route('mysection', '/mysection*subpath')
你可以填写 *subpath
使用价值 resource_url
通过做:
request.resource_path(a, route_name='mysection',
route_remainder_name='subpath')
如果你通过 route_remainder_name
但不通过 route_name
, route_remainder_name
将被忽略。
如果你想用 resource_path
或 resource_url
当 route_name
参数指向一个没有剩余starg的路由,不会引发错误,但生成的URL也不会包含任何剩余信息。
通常可以通过的所有其他值 resource_path
和 resource_url
(如 query
, anchor
, host
, port
,和位置元素)按您在此配置中的预期工作。
请注意,此功能与 __resource_url__
特征(见) 覆盖资源URL生成 )在资源对象上实现。任何 __resource_url__
当您通过时,将忽略您的资源提供的 route_name
.