资源

A resource 表示与应用程序相关的树中的“位置”的对象。每个 Pyramid 应用程序至少有一个资源对象: root 资源。即使您没有手动定义根资源,也会为您创建一个默认的根资源。根资源是 resource tree . 资源树是一组嵌套的类似字典的对象,可以用来表示网站的结构。

在使用 traversal 为了将URL映射到代码,大量使用资源树结构将每个URL映射到 view callable .什么时候? traversal 使用, Pyramid 将遍历资源树的嵌套字典结构以查找 context 资源。一旦找到上下文资源,请求中的上下文资源和数据将用于查找 view callable .

在使用 URL dispatch ,资源树只是间接使用的,开发人员经常“看不见”。在URL调度应用程序中,资源“树”通常只由根资源本身组成。这个根资源有时会附加安全声明,但不需要附加任何安全声明。一般来说,在使用URL调度的应用程序中,资源树比使用遍历的应用程序要重要得多。

在“Zope” Pyramid 应用程序、资源对象通常也会持久地存储数据,并提供与改变持久数据相关的方法。在这类应用程序中,资源不仅代表网站的网站结构,而且成为 domain model 应用程序的。

也:

定义资源树

什么时候? traversal 使用(与纯 URL dispatch 基于应用程序) Pyramid 期望能够遍历由资源组成的树 resource tree )遍历从根资源开始,然后递归地下降到树中,尝试每个资源的 __getitem__ 方法将路径段解析为另一个资源对象。 Pyramid 对树中的资源实例实施以下策略:

  • 容器资源(包含其他资源的资源)必须提供 __getitem__ 愿意将Unicode名称解析为子资源的方法。如果容器资源中不存在具有特定名称的子资源,则 __getitem__ 容器资源的方法必须引发 KeyError . 如果使用该名称的子资源 does 存在,容器的 __getitem__ 应返回子资源。
  • 不包含其他资源的叶资源不能实现 __getitem__ 或者,如果他们这样做,他们的 __getitem__ 方法必须始终引发 KeyError .

遍历 有关遍历如何处理资源实例的详细信息。

这是一个示例资源树,由一个名为 root

1
2
3
4
class Resource(dict):
    pass

root = Resource({'a':Resource({'b':Resource({'c':Resource()})})})

我们在上面创建的资源树是由一个类似字典的根对象表示的,它有一个子对象,名为 'a' . 'a' 有一个孩子叫 'b''b' 有一个孩子叫 'c' 没有孩子。因此可以访问 'c' 像这样的叶资源:

1
root['a']['b']['c']

如果你还了上面的 root 来自A的对象 root factory 这条路 /a/b/c 会找到 'c' 资源树中的对象,作为 traversal .

在本例中,树中的每个资源都属于同一类。这不是要求。树中的资源元素可以是任何类型。为了简单起见,我们使用了一个类来表示树中的所有资源,但是在“真实”应用程序中,树中的资源可以是任意的。

尽管上面的示例树可以服务于遍历,但是上面示例中的资源实例不知道 location 因此,它们在“真实”应用程序中的效用是有限的。充分利用内置 Pyramid API设施,您的资源应该是“位置感知”。下一节详细介绍如何使资源位置感知。

位置感知资源

为了确定 Pyramid 位置、安全性、URL生成和遍历API要针对资源树中的资源正常工作,树中的所有资源都必须 location 意识到。这意味着它们必须具有两个属性: __parent____name__ .

这个 __parent__ 位置感知资源的属性应该是对树中资源的父资源实例的引用。这个 __name__ 属性应该是资源的父级通过其引用资源的名称 __getitem__ .

这个 __parent__ 根资源的 None 及其 __name__ 应该是空字符串。例如:

1
2
3
class MyRootResource(object):
    __name__ = ''
    __parent__ = None

从根资源返回的资源 __getitem__ 方法应具有 __parent__ 作为对根资源的引用的属性,及其 __name__ 属性应与通过根资源可访问的名称匹配 __getitem__ . 根资源中的容器资源应具有 __getitem__ 返回带有 __parent__ 指向容器的属性,这些子对象应具有 __name__ 属性,该属性与通过其从容器中检索它们的名称相匹配 __getitem__ . 这个模式从根目录继续递归地“向上”树。

这个 __parent__ attributes of each resource form a linked list that points "downwards" toward the root. This is analogous to the .. 文件系统目录中的条目。如果你跟着 __parent__ values from any resource in the resource tree, you will eventually come to the root resource, just like if you keep executing the cd .. filesystem命令,最终您将到达filesystem根目录。

警告

如果根资源具有 __name__ 不是的论点 None 或空字符串,由 resource_url() 函数和由 resource_path()resource_path_tuple() 将不正确地生成API。价值 __name__ 将在生成的每个路径和URL前面加上前缀(而不是一个前导斜杠或空元组元素)。

使用树行走的应用程序 Pyramid API需要位置感知资源。这些API包括(但不限于) resource_url()find_resource()find_root()find_interface()resource_path()resource_path_tuple()traverse()virtual_root() ,以及(通常) has_permission()principals_allowed_by_permission() .

一般来说,因为 Pyramid 基础设施依赖于位置感知资源,最好让树中的每个资源都知道位置。

生成资源的URL

如果你的资源是 location -注意,您可以使用 pyramid.request.Request.resource_url() 为资源生成URL的API。此URL将使用资源在父树中的位置来创建资源路径,并将该路径的前缀设置为当前应用程序URL,以形成具有方案、主机、端口和路径的完全限定的URL。您还可以向 resource_url() 影响生成的URL。

最简单的呼叫 resource_url() 如下所示:

1
url = request.resource_url(resource)

这个 request 在上面的示例中是 Pyramid request 对象。

如果资源被称为 resource 在上面的示例中是根资源,用于联系服务器的主机是 example.com ,生成的URL将是 http://example.com/ . 但是,如果资源是名为 a ,生成的URL将是 http://example.com/a/ .

resource_url() 用于以这种简单的方式生成它们,因为资源是层次结构中的“位置”,而URL是要单击以进行访问的。在作为资源默认视图呈现的HTML页面上包含的相对URL更倾向于相对于这些资源,而不是相对于其父资源。

您还可以将额外的元素传递给 resource_url()

1
url = request.resource_url(resource, 'foo', 'bar')

如果资源被称为 resource 在上面的示例中是根资源,用于联系服务器的主机是 example.com ,生成的URL将是 http://example.com/foo/bar . 可以将任意数量的额外元素传递给 resource_url() 作为额外的位置参数。当传递额外的元素时,它们将被附加到资源的URL中。传递元素时,斜线不会附加到最后一段。

还可以传递查询字符串:

1
url = request.resource_url(resource, query={'a':'1'})

如果资源被称为 resource 在上面的示例中是根资源,用于联系服务器的主机是 example.com ,生成的URL将是 http://example.com/?a=1 .

当A virtual root 是活动的,由 resource_url() 因为资源可能比其物理树路径“短”。见 虚拟根支持 有关虚拟根资源的更多信息。

有关生成资源URL的详细信息,请参阅 pyramid.request.Request.resource_url() .

覆盖资源URL生成

如果资源对象实现 __resource_url__ 方法,此方法将在 resource_url() 调用以生成资源的URL,重写为该资源返回的默认URL resource_url() .

这个 __resource_url__ 钩子传递了两个参数: requestinfo . requestrequest 传递给的对象 resource_url() . info 是具有以下键的字典:

physical_path
表示为资源计算的“物理路径”的字符串,由定义 pyramid.traversal.resource_path(resource) . 它将以斜线开始和结束。
virtual_path
表示为资源计算的“虚拟路径”的字符串,由定义 虚拟根支持 . 如果未启用虚拟根目录,这将与物理路径相同。它将以斜线开始和结束。
app_url
一个字符串,表示在 request.resource_url . 它不会以斜线结尾。它表示一个可能自定义的URL前缀,其中包含用户传递给的可能自定义方案、主机和端口信息。 request.resource_url . 它应该比使用 request.application_url .

这个 __resource_url__ 资源的方法应返回表示URL的字符串。如果它不能覆盖默认值,它应该返回 None . 如果它回来 None ,将返回默认的URL。

下面是一个例子 __resource_url__ 方法。

1
2
3
class Resource(object):
    def __resource_url__(self, request, info):
        return info['app_url'] + info['virtual_path']

上面的示例实际上只是生成并返回默认的URL,这将是默认生成的URL。 resource_url 但您的代码可以根据需要执行任意逻辑。例如,您的代码可能希望覆盖生成的URL的主机名或端口号。

请注意,由生成的URL __resource_url__ 应该是完全限定的,应该以斜杠结尾,并且不应该包含任何要使用的查询字符串或定位元素(仅限路径元素) resource_url() .

生成资源路径

pyramid.traversal.resource_path() 返回一个字符串对象,该对象根据资源对象在资源树中的位置表示该资源对象的绝对物理路径。路径的每一段用斜线字符分隔。

1
2
from pyramid.traversal import resource_path
url = resource_path(resource)

如果 resource 在上面的示例中,可以在树中访问 root['a']['b'] ,上面的示例将生成字符串 /a/b .

传递给的任何位置参数 resource_path() 将作为路径段附加到资源路径的末尾。

1
2
from pyramid.traversal import resource_path
url = resource_path(resource, 'foo', 'bar')

如果 resource 在上面的示例中,可以在树中访问 root['a']['b'] ,上面的示例将生成字符串 /a/b/foo/bar .

传入的资源必须是 location 意识到。

存在或不存在 virtual root 对…的行为没有影响 resource_path() .

按路径查找资源

如果有资源的字符串路径,则可以使用 pyramid.traversal.find_resource() .

通过传递前缀为 / 作为 path 论点:

1
2
from pyramid.traversal import find_resource
url = find_resource(anyresource, '/path')

或者,您可以解析一个与您传入的资源相关的路径 pyramid.traversal.find_resource() 通过传递一个不带前缀的字符串 /

1
2
from pyramid.traversal import find_resource
url = find_resource(anyresource, 'path')

通常是你经过的路 find_resource() 是由 resource_path() 应用程序编程接口。这些API是彼此的“镜像”。

如果调用时无法解析路径 find_resource() (如果树中的相应资源不存在),a KeyError 将被提升。

pyramid.traversal.find_resource() 有关解析资源路径的详细信息,请参阅文档。

获取资源的沿袭

pyramid.location.lineage() 返回表示 lineagelocation 意识到的 resource 对象。

这个 lineage() 函数返回传递给它的资源,然后按顺序返回资源的每个父级。例如,如果资源树是这样组成的:

1
2
3
4
5
class Thing(object): pass

thing1 = Thing()
thing2 = Thing()
thing2.__parent__ = thing1

调用 lineage(thing2) 将返回发电机。当我们把它变成一个列表时,我们会得到:

1
2
list(lineage(thing2))
[ <Thing object at thing2>, <Thing object at thing1> ]

发电机返回 lineage() 首先无条件返回传递给它的资源。然后,如果资源提供了 __parent__ 属性,它返回由 resource.__parent__ . 如果 that 资源有 __parent__ 属性,它将返回该资源的父级,依此类推,直到被检查的资源没有 __parent__ 属性或具有 __parent__ 属性 None .

参见文档 pyramid.location.lineage() 更多信息。

确定资源是否在另一个资源的谱系中

使用 pyramid.location.inside() 函数确定一个资源是否在 lineage 其他资源。

例如,如果资源树是:

1
2
3
4
5
class Thing(object): pass

a = Thing()
b = Thing()
b.__parent__ = a

调用 inside(b, a) 将返回 True ,因为 b 有一个血统,包括 a . 但是,打电话 inside(a, b) 将返回 False 因为 a 没有包括 b .

的参数列表 inside()(resource1, resource2) . resource1 是“内” resource2 如果 resource2 是一个 lineage 祖先 resource1 . 如果它的父母(或它的父母之一等)是祖先,它就是世系祖先。

pyramid.location.inside() 更多信息。

查找根资源

使用 pyramid.traversal.find_root() API查找 root 资源。根资源是位于 resource tree . API接受单个参数: resource . 这是一个资源 location 意识到。它可以是树中要查找其根的任何资源。

例如,如果资源树是:

1
2
3
4
5
class Thing(object): pass

a = Thing()
b = Thing()
b.__parent__ = a

调用 find_root(b) 将返回 a .

根资源也可用作 request.root 在内部 view callable 代码。

存在或不存在 virtual root 对…的行为没有影响 find_root() . 返回的根对象始终是 身体的 根对象。

实现接口的资源

可以选择使用资源来实现 interface . 接口用于用“类型”标记资源对象,稍后可以在 view configuration 并且通过 pyramid.traversal.find_interface() .

将接口而不是类指定为 contextcontainment 谓词参数 view configuration 语句使得可以对多个资源对象类使用可调用的单个视图。如果您的应用程序足够简单,以至于您看不到想要这样做的理由,那么您可以跳过阅读本章的这一部分。

例如,下面的一些代码描述了一个博客条目,它还声明博客条目实现了一个 interface .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import datetime
from zope.interface import implementer
from zope.interface import Interface

class IBlogEntry(Interface):
    pass

@implementer(IBlogEntry)
class BlogEntry(object):
    def __init__(self, title, body, author):
        self.title = title
        self.body = body
        self.author = author
        self.created = datetime.datetime.now()

这个资源由两部分组成:将资源构造函数定义为类的类 BlogEntry 和一个 interface 通过一个 implementer 类装饰器使用 IBlogEntry 接口作为其唯一参数。

使用的接口对象必须是继承自的类的实例 zope.interface.Interface .

资源类可以实现零个或多个接口。指定资源通过使用 zope.interface.implementer() 作为类修饰器。以上 BlogEntry 资源实现 IBlogEntry 接口。

还可以指定特定资源 实例 提供一个接口,而不是它的类。当您声明类实现接口时,该类的所有实例也将提供该接口。但是,也可以说单个对象提供了接口。为此,请使用 zope.interface.directlyProvides() 功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import datetime
from zope.interface import directlyProvides
from zope.interface import Interface

class IBlogEntry(Interface):
    pass

class BlogEntry(object):
    def __init__(self, title, body, author):
        self.title = title
        self.body = body
        self.author = author
        self.created = datetime.datetime.now()

entry = BlogEntry('title', 'body', 'author')
directlyProvides(entry, IBlogEntry)

zope.interface.directlyProvides() 将替换以前由实例提供的任何现有接口。如果一个资源对象已经有了您不想替换的实例级接口声明,请使用 zope.interface.alsoProvides() 功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import datetime
from zope.interface import alsoProvides
from zope.interface import directlyProvides
from zope.interface import Interface

class IBlogEntry1(Interface):
    pass

class IBlogEntry2(Interface):
    pass

class BlogEntry(object):
    def __init__(self, title, body, author):
        self.title = title
        self.body = body
        self.author = author
        self.created = datetime.datetime.now()

entry = BlogEntry('title', 'body', 'author')
directlyProvides(entry, IBlogEntry1)
alsoProvides(entry, IBlogEntry2)

zope.interface.alsoProvides() 将扩充实例直接提供的接口集,而不是像这样覆盖它们 zope.interface.directlyProvides() 做。

有关视图配置如何使用资源接口的详细信息,请参阅 在视图配置中使用资源接口 .

在沿袭中查找具有类或接口的资源

使用 find_interface() 用于定位属于特定python类或实现某些 interface .

例如,如果资源树由以下内容组成:

1
2
3
4
5
6
class Thing1(object): pass
class Thing2(object): pass

a = Thing1()
b = Thing2()
b.__parent__ = a

调用 find_interface(a, Thing1) 将返回 a 资源,因为 a 是一流的 Thing1 (作为第一个参数传递的资源首先被考虑,如果类或接口规范匹配,则返回该资源)。

调用 find_interface(b, Thing1) 将返回 a 资源,因为 a 是一流的 Thing1a 是第一个资源 b 这个阶级的血统。

调用 find_interface(b, Thing2) 将返回 b 资源。

第二个论点 find_interface 也可能是 interface 而不是类。如果是接口,则检查沿袭中的每个资源,以查看资源是否实现特定的接口(而不是查看资源是否属于类)。

参见

也见 实现接口的资源 .

Pyramid 针对资源的API函数

资源对象用作 context 提供给视图。见 遍历URL调度 有关资源对象如何成为上下文的详细信息。

由提供的API pyramid.traversal 用于资源对象。这些函数可用于查找资源的“路径”、资源树中的根资源,或生成资源的URL。

由提供的API pyramid.location 用于资源。这些可以用来沿着资源树走下去,或者方便地在另一个资源中定位一个资源。

上的一些API pyramid.request.Request 接受资源对象作为参数。例如, has_permission() API接受一个资源对象作为它的参数之一;ACL是从这个资源或它的一个祖先获得的。上的其他安全相关API pyramid.request.Request 课程也接受 context 作为一个参数,上下文总是一个资源。