A resource
is an object that represents a "place" in a tree related to your application. Every Pyramid
application has at least one resource object: the root
resource. Even if you don't define a root resource manually, a default one is created for you. The root resource is the root of a resource tree
. A resource tree is a set of nested dictionary-like objects which you can use to represent your website's structure.
In an application which uses traversal
to map URLs to code, the resource tree structure is used heavily to map each URL to a view
callable
. When traversal
is used, Pyramid
will walk through the resource tree by traversing through its nested dictionary structure in order to find a context
resource. Once a context resource is found, the context resource and data in the request will be used to find a view callable
.
In an application which uses URL dispatch
, the resource tree is only used indirectly, and is often "invisible" to the developer. In URL dispatch applications, the resource "tree" is often composed of only the root resource by itself. This root resource sometimes has security declarations attached to it, but is not required to have any. In general, the resource tree is much less important in applications that use URL dispatch than applications that use traversal.
In "Zope-like" Pyramid
applications, resource objects also often store data persistently, and offer methods related to mutating that persistent data. In these kinds of applications, resources not only represent the site structure of your website, but they become the domain model
of the application.
Also:
- The
context
andcontainment
predicate arguments to~pyramid.config.Configurator.add_view
(or a~pyramid.view.view_config
decorator) reference a resource class or resourceinterface
. - A
root factory
returns a resource. - A resource is exposed to
view
code as thecontext
of a view. - Various helpful
Pyramid
API methods expect a resource as an argument (e.g.~pyramid.url.resource_url
and others).
single: resource tree single: traversal tree single: object tree single: container resources single: leaf resources
When traversal
is used (as opposed to a purely url dispatch
based application), Pyramid
expects to be able to traverse a tree composed of resources (the resource tree
). Traversal begins at a root resource, and descends into the tree recursively, trying each resource's __getitem__
method to resolve a path segment to another resource object. Pyramid
imposes the following policy on resource instances in the tree:
- A container resource (a resource which contains other resources) must supply a
__getitem__
method which is willing to resolve a unicode name to a sub-resource. If a sub-resource by a particular name does not exist in a container resource,__getitem__
method of the container resource must raise aKeyError
. If a sub-resource by that name does exist, the container's__getitem__
should return the sub-resource. - Leaf resources, which do not contain other resources, must not implement a
__getitem__
, or if they do, their__getitem__
method must always raise aKeyError
.
See traversal_chapter
for more information about how traversal works against resource instances.
Here's a sample resource tree, represented by a variable named root
:
class Resource(dict):
pass
root = Resource({'a':Resource({'b':Resource({'c':Resource()})})})
The resource tree we've created above is represented by a dictionary-like root object which has a single child named 'a'
. 'a'
has a single child named 'b'
, and 'b'
has a single child named 'c'
, which has no children. It is therefore possible to access the 'c'
leaf resource like so:
root['a']['b']['c']
If you returned the above root
object from a root factory
, the path /a/b/c
would find the 'c'
object in the resource tree as the result of traversal
.
In this example, each of the resources in the tree is of the same class. This is not a requirement. Resource elements in the tree can be of any type. We used a single class to represent all resources in the tree for the sake of simplicity, but in a "real" app, the resources in the tree can be arbitrary.
Although the example tree above can service a traversal, the resource instances in the above example are not aware of location
, so their utility in a "real" application is limited. To make best use of built-in Pyramid
API facilities, your resources should be "location-aware". The next section details how to make resources location-aware.
pair: location-aware; resource
In order for certain Pyramid
location, security, URL-generation, and traversal APIs to work properly against the resources in a resource tree, all resources in the tree must be location
-aware. This means they must have two attributes: __parent__
and __name__
.
The __parent__
attribute of a location-aware resource should be a reference to the resource's parent resource instance in the tree. The __name__
attribute should be the name with which a resource's parent refers to the resource via __getitem__
.
The __parent__
of the root resource should be None
and its __name__
should be the empty string. For instance:
class MyRootResource(object):
__name__ = ''
__parent__ = None
A resource returned from the root resource's __getitem__
method should have a __parent__
attribute that is a reference to the root resource, and its __name__
attribute should match the name by which it is reachable via the root resource's __getitem__
. A container resource within the root resource should have a __getitem__
that returns resources with a __parent__
attribute that points at the container, and these subobjects should have a __name__
attribute that matches the name by which they are retrieved from the container via __getitem__
. This pattern continues recursively "up" the tree from the root.
The __parent__
attributes of each resource form a linked list that points "downwards" toward the root. This is analogous to the .. entry in filesystem directories. If you follow 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 command, eventually you will reach the filesystem root directory.
Warning
If your root resource has a __name__
argument that is not None
or the empty string, URLs returned by the ~pyramid.url.resource_url
function and paths generated by the ~pyramid.traversal.resource_path
and ~pyramid.traversal.resource_path_tuple
APIs will be generated improperly. The value of __name__
will be prepended to every path and URL generated (as opposed to a single leading slash or empty tuple element).
Using pyramid_traversalwrapper
If you'd rather not manage the __name__
and __parent__
attributes of your resources "by hand", an add-on package named pyramid_traversalwrapper
can help.
In order to use this helper feature, you must first install the pyramid_traversalwrapper
package (available via PyPI), then register its ModelGraphTraverser
as the traversal policy, rather than the default Pyramid
traverser. The package contains instructions for doing so.
Once Pyramid
is configured with this feature, you will no longer need to manage the __parent__
and __name__
attributes on resource objects "by hand". Instead, as necessary, during traversal Pyramid
will wrap each resource (even the root resource) in a LocationProxy
which will dynamically assign a __name__
and a __parent__
to the traversed resource (based on the last traversed resource and the name supplied to __getitem__
). The root resource will have a __name__
attribute of None
and a __parent__
attribute of None
.
Applications which use tree-walking Pyramid
APIs require location-aware resources. These APIs include (but are not limited to) ~pyramid.url.resource_url
, ~pyramid.traversal.find_resource
, ~pyramid.traversal.find_root
, ~pyramid.traversal.find_interface
, ~pyramid.traversal.resource_path
, ~pyramid.traversal.resource_path_tuple
, or ~pyramid.traversal.traverse
, ~pyramid.traversal.virtual_root
, and (usually) ~pyramid.security.has_permission
and ~pyramid.security.principals_allowed_by_permission
.
In general, since so much Pyramid
infrastructure depends on location-aware resources, it's a good idea to make each resource in your tree location-aware.
single: resource_url pair: generating; resource url
If your resources are location
aware, you can use the pyramid.url.resource_url
API to generate a URL for the resource. This URL will use the resource's position in the parent tree to create a resource path, and it will prefix the path with the current application URL to form a fully-qualified URL with the scheme, host, port, and path. You can also pass extra arguments to ~pyramid.url.resource_url
to influence the generated URL.
The simplest call to ~pyramid.url.resource_url
looks like this:
from pyramid.url import resource_url
url = resource_url(resource, request)
The request
passed to resource_url
in the above example is an instance of a Pyramid
request
object.
If the resource referred to as resource
in the above example was the root resource, and the host that was used to contact the server was example.com
, the URL generated would be http://example.com/
. However, if the resource was a child of the root resource named a
, the generated URL would be http://example.com/a/
.
A slash is appended to all resource URLs when ~pyramid.url.resource_url
is used to generate them in this simple manner, because resources are "places" in the hierarchy, and URLs are meant to be clicked on to be visited. Relative URLs that you include on HTML pages rendered as the result of the default view of a resource are more apt to be relative to these resources than relative to their parent.
You can also pass extra elements to ~pyramid.url.resource_url
:
from pyramid.url import resource_url
url = resource_url(resource, request, 'foo', 'bar')
If the resource referred to as resource
in the above example was the root resource, and the host that was used to contact the server was example.com
, the URL generated would be http://example.com/foo/bar
. Any number of extra elements can be passed to ~pyramid.url.resource_url
as extra positional arguments. When extra elements are passed, they are appended to the resource's URL. A slash is not appended to the final segment when elements are passed.
You can also pass a query string:
from pyramid.url import resource_url
url = resource_url(resource, request, query={'a':'1'})
If the resource referred to as resource
in the above example was the root resource, and the host that was used to contact the server was example.com
, the URL generated would be http://example.com/?a=1
.
When a virtual root
is active, the URL generated by ~pyramid.url.resource_url
for a resource may be "shorter" than its physical tree path. See virtual_root_support
for more information about virtually rooting a resource.
The shortcut method of the request
named pyramid.request.Request.resource_url
can be used instead of ~pyramid.url.resource_url
to generate a resource URL.
For more information about generating resource URLs, see the documentation for pyramid.url.resource_url
.
pair: resource URL generation; overriding
If a resource object implements a __resource_url__
method, this method will be called when ~pyramid.url.resource_url
is called to generate a URL for the resource, overriding the default URL returned for the resource by ~pyramid.url.resource_url
.
The __resource_url__
hook is passed two arguments: request
and info
. request
is the request
object passed to ~pyramid.url.resource_url
. info
is a dictionary with two keys:
physical_path
The "physical path" computed for the resource, as defined by
pyramid.traversal.resource_path(resource)
.virtual_path
The "virtual path" computed for the resource, as defined by
virtual_root_support
. This will be identical to the physical path if virtual rooting is not enabled.
The __resource_url__
method of a resource should return a string representing a URL. If it cannot override the default, it should return None
. If it returns None
, the default URL will be returned.
Here's an example __resource_url__
method.
class Resource(object):
def __resource_url__(self, request, info):
return request.application_url + info['virtual_path']
The above example actually just generates and returns the default URL, which would have been what was returned anyway, but your code can perform arbitrary logic as necessary. For example, your code may wish to override the hostname or port number of the generated URL.
Note that the URL generated by __resource_url__
should be fully qualified, should end in a slash, and should not contain any query string or anchor elements (only path elements) to work best with ~pyramid.url.resource_url
.
single: resource path generation
pyramid.traversal.resource_path
returns a string object representing the absolute physical path of the resource object based on its position in the resource tree. Each segment of the path is separated with a slash character.
from pyramid.traversal import resource_path
url = resource_path(resource)
If resource
in the example above was accessible in the tree as root['a']['b']
, the above example would generate the string /a/b
.
Any positional arguments passed in to ~pyramid.traversal.resource_path
will be appended as path segments to the end of the resource path.
from pyramid.traversal import resource_path
url = resource_path(resource, 'foo', 'bar')
If resource
in the example above was accessible in the tree as root['a']['b']
, the above example would generate the string /a/b/foo/bar
.
The resource passed in must be location
-aware.
The presence or absence of a virtual root
has no impact on the behavior of ~pyramid.traversal.resource_path
.
pair: resource; finding by path
If you have a string path to a resource, you can grab the resource from that place in the application's resource tree using pyramid.traversal.find_resource
.
You can resolve an absolute path by passing a string prefixed with a /
as the path
argument:
from pyramid.traversal import find_resource
url = find_resource(anyresource, '/path')
Or you can resolve a path relative to the resource you pass in by passing a string that isn't prefixed by /
:
from pyramid.traversal import find_resource
url = find_resource(anyresource, 'path')
Often the paths you pass to ~pyramid.traversal.find_resource
are generated by the ~pyramid.traversal.resource_path
API. These APIs are "mirrors" of each other.
If the path cannot be resolved when calling ~pyramid.traversal.find_resource
(if the respective resource in the tree does not exist), a KeyError
will be raised.
See the pyramid.traversal.find_resource
documentation for more information about resolving a path to a resource.
pair: resource; lineage
pyramid.location.lineage
returns a generator representing the lineage
of the location
aware resource
object.
The ~pyramid.location.lineage
function returns the resource it is passed, then each parent of the resource, in order. For example, if the resource tree is composed like so:
class Thing(object): pass
thing1 = Thing()
thing2 = Thing()
thing2.__parent__ = thing1
Calling lineage(thing2)
will return a generator. When we turn it into a list, we will get:
list(lineage(thing2))
[ <Thing object at thing2>, <Thing object at thing1> ]
The generator returned by ~pyramid.location.lineage
first returns the resource it was passed unconditionally. Then, if the resource supplied a __parent__
attribute, it returns the resource represented by resource.__parent__
. If that resource has a __parent__
attribute, return that resource's parent, and so on, until the resource being inspected either has no __parent__
attribute or has a __parent__
attribute of None
.
See the documentation for pyramid.location.lineage
for more information.
Use the pyramid.location.inside
function to determine if one resource is in the lineage
of another resource.
For example, if the resource tree is:
class Thing(object): pass
a = Thing()
b = Thing()
b.__parent__ = a
Calling inside(b, a)
will return True
, because b
has a lineage that includes a
. However, calling inside(a, b)
will return False
because a
does not have a lineage that includes b
.
The argument list for ~pyramid.location.inside
is (resource1, resource2)
. resource1
is 'inside' resource2
if resource2
is a lineage
ancestor of resource1
. It is a lineage ancestor if its parent (or one of its parent's parents, etc.) is an ancestor.
See pyramid.location.inside
for more information.
pair: resource; finding root
Use the pyramid.traversal.find_root
API to find the root
resource. The root resource is the root resource of the resource
tree
. The API accepts a single argument: resource
. This is a resource that is location
aware. It can be any resource in the tree for which you want to find the root.
For example, if the resource tree is:
class Thing(object): pass
a = Thing()
b = Thing()
b.__parent__ = a
Calling find_root(b)
will return a
.
The root resource is also available as request.root
within view
callable
code.
The presence or absence of a virtual root
has no impact on the behavior of ~pyramid.traversal.find_root
. The root object returned is always the physical root object.
single: resource interfaces
Resources can optionally be made to implement an interface
. An interface is used to tag a resource object with a "type" that can later be referred to within view configuration
and by pyramid.traversal.find_interface
.
Specifying an interface instead of a class as the context
or containment
predicate arguments within view configuration
statements makes it possible to use a single view callable for more than one class of resource object. If your application is simple enough that you see no reason to want to do this, you can skip reading this section of the chapter.
For example, here's some code which describes a blog entry which also declares that the blog entry implements an interface
.
import datetime
from zope.interface import implements
from zope.interface import Interface
class IBlogEntry(Interface):
pass
class BlogEntry(object):
implements(IBlogEntry)
def __init__(self, title, body, author):
self.title = title
self.body = body
self.author = author
self.created = datetime.datetime.now()
This resource consists of two things: the class which defines the resource constructor as the class BlogEntry
, and an interface
attached to the class via an implements
statement at class scope using the IBlogEntry
interface as its sole argument.
The interface object used must be an instance of a class that inherits from zope.interface.Interface
.
A resource class may implement zero or more interfaces. You specify that a resource implements an interface by using the zope.interface.implements
function at class scope. The above BlogEntry
resource implements the IBlogEntry
interface.
You can also specify that a particular resource instance provides an interface, as opposed to its class. When you declare that a class implements an interface, all instances of that class will also provide that interface. However, you can also just say that a single object provides the interface. To do so, use the zope.interface.directlyProvides
function:
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
will replace any existing interface that was previously provided by an instance. If a resource object already has instance-level interface declarations that you don't want to replace, use the zope.interface.alsoProvides
function:
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
will augment the set of interfaces directly provided by an instance instead of overwriting them like zope.interface.directlyProvides
does.
For more information about how resource interfaces can be used by view configuration, see using_resource_interfaces
.
pair: resource; finding by interface or class
Use the ~pyramid.traversal.find_interface
API to locate a parent that is of a particular Python class, or which implements some interface
.
For example, if your resource tree is composed as follows:
class Thing1(object): pass
class Thing2(object): pass
a = Thing1()
b = Thing2()
b.__parent__ = a
Calling find_interface(a, Thing1)
will return the a
resource because a
is of class Thing1
(the resource passed as the first argument is considered first, and is returned if the class or interface spec matches).
Calling find_interface(b, Thing1)
will return the a
resource because a
is of class Thing1
and a
is the first resource in b
's lineage of this class.
Calling find_interface(b, Thing2)
will return the b
resource.
The second argument to find_interface may also be a interface
instead of a class. If it is an interface, each resource in the lineage is checked to see if the resource implements the specificed interface (instead of seeing if the resource is of a class). See also resources_which_implement_interfaces
.
single: resource API functions single: url generation (traversal)
A resource object is used as the context
provided to a view. See traversal_chapter
and urldispatch_chapter
for more information about how a resource object becomes the context.
The APIs provided by traversal_module
are used against resource objects. These functions can be used to find the "path" of a resource, the root resource in a resource tree, or to generate a URL for a resource.
The APIs provided by location_module
are used against resources. These can be used to walk down a resource tree, or conveniently locate one resource "inside" another.
Some APIs in security_module
accept a resource object as a parameter. For example, the ~pyramid.security.has_permission
API accepts a resource object as one of its arguments; the ACL is obtained from this resource or one of its ancestors. Other APIs in the pyramid.security
module also accept context
as an argument, and a context is always a resource.