summaryrefslogtreecommitdiff
path: root/docs/narr/resources.rst
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2010-12-18 02:27:14 -0500
committerChris McDonough <chrism@plope.com>2010-12-18 02:27:14 -0500
commitfb6a5ce52a275f7798e82a34b5907ea118cbd2ff (patch)
tree29e80d0eb681676d1e0eb2707ca764dbf5491864 /docs/narr/resources.rst
parentbf89764a7e3ccab6133c9ad43b8d9af4f5c4083b (diff)
downloadpyramid-fb6a5ce52a275f7798e82a34b5907ea118cbd2ff.tar.gz
pyramid-fb6a5ce52a275f7798e82a34b5907ea118cbd2ff.tar.bz2
pyramid-fb6a5ce52a275f7798e82a34b5907ea118cbd2ff.zip
model -> resource; resource -> asset
Diffstat (limited to 'docs/narr/resources.rst')
-rw-r--r--docs/narr/resources.rst465
1 files changed, 255 insertions, 210 deletions
diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst
index c87157472..05e108494 100644
--- a/docs/narr/resources.rst
+++ b/docs/narr/resources.rst
@@ -1,274 +1,319 @@
-.. index::
- single: resources
-
-.. _resources_chapter:
-
Resources
=========
-A :term:`resource` is any file contained within a Python
-:term:`package` which is *not* a Python source code file. For
-example, each of the following is a resource:
-
-- a :term:`Chameleon` template file contained within a Python package.
-
-- a GIF image file contained within a Python package.
-
-- a CSS file contained within a Python package.
-
-- a JavaScript source file contained within a Python package.
-
-- A directory within a package that does not have an ``__init__.py``
- in it (if it possessed an ``__init__.py`` it would *be* a package).
+A :term:`resource` is an object that represents a "place" in your
+application. Every :app:`Pyramid` application has at least one resource
+object: the :term:`root resource`. The root resource is the root of a
+:term:`resource tree`. A resource tree is a set of nested dictionary-like
+objects which you may use to represent your website's structure.
+
+In an application which uses :term:`traversal` to map URLs to code, the
+resource tree structure is used heavily to map a URL to a :term:`view
+callable`. :app:`Pyramid` will walk "up" the resource tree when
+:term:`traversal` is used in order to find a :term:`context`. Once a context
+is found, the resource represented by the context combined with data in the
+request will be used to find a :term:`view callable`.
+
+In an application which uses :term:`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" :app:`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 :term:`model` of the
+application.
+
+Also:
+
+- The ``context`` and ``containment`` predicate arguments to
+ :meth:`pyramid.config.Configurator.add_view` (or a
+ :func:`pyramid.view.view_config` decorator) and reference a resource class
+ or resource :term:`interface`.
+
+- A :term:`root factory` returns a resource.
+
+- A resource is exposed to :term:`view` code as the :term:`context` of a
+ view.
-The use of resources is quite common in most web development projects.
-For example, when you create a :app:`Pyramid` application using one
-of the available "paster" templates, as described in
-:ref:`creating_a_project`, the directory representing the application
-contains a Python :term:`package`. Within that Python package, there
-are directories full of files which are resources. For example, there
-is a ``templates`` directory which contains ``.pt`` files, and a
-``static`` directory which contains ``.css``, ``.js``, and ``.gif``
-files.
-
-.. _understanding_resources:
+.. index::
+ single: resource constructor
-Understanding Resources
------------------------
+Defining a Resource Constructor
+-------------------------------
-Let's imagine you've created a :app:`Pyramid` application that uses
-a :term:`Chameleon` ZPT template via the
-:func:`pyramid.chameleon_zpt.render_template_to_response` API. For
-example, the application might address the resource named
-``templates/some_template.pt`` using that API within a ``views.py``
-file inside a ``myapp`` package:
+An example of a resource constructor, ``BlogEntry`` is presented below. It
+is implemented as a class which, when instantiated, becomes a resource
+instance.
-.. ignore-next-block
.. code-block:: python
:linenos:
- from pyramid.chameleon_zpt import render_template_to_response
- render_template_to_response('templates/some_template.pt')
-
-"Under the hood", when this API is called, :app:`Pyramid` attempts
-to make sense out of the string ``templates/some_template.pt``
-provided by the developer. To do so, it first finds the "current"
-package. The "current" package is the Python package in which the
-``views.py`` module which contains this code lives. This would be the
-``myapp`` package, according to our example so far. By resolving the
-current package, :app:`Pyramid` has enough information to locate
-the actual template file. These are the elements it needs:
-
-- The *package name* (``myapp``)
+ import datetime
-- The *resource name* (``templates/some_template.pt``)
+ class BlogEntry(object):
+ def __init__(self, title, body, author):
+ self.title = title
+ self.body = body
+ self.author = author
+ self.created = datetime.datetime.now()
-:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the
-package name and resource name to an absolute
-(operating-system-specific) file name. It eventually passes this
-resolved absolute filesystem path to the Chameleon templating engine,
-which then uses it to load, parse, and execute the template file.
-
-Package names often contain dots. For example, ``pyramid`` is a
-package. Resource names usually look a lot like relative UNIX file
-paths.
+A resource constructor may be any Python object which is callable, and which
+returns a resource instance. In the above example, the ``BlogEntry`` class
+can be "called", returning a resource instance.
.. index::
- pair: overriding; resources
+ single: resource interfaces
-.. _overriding_resources_section:
+.. _resources_which_implement_interfaces:
-Overriding Resources
---------------------
+Resources Which Implement Interfaces
+------------------------------------
-It can often be useful to override specific resources "from outside" a
-given :app:`Pyramid` application. For example, you may wish to
-reuse an existing :app:`Pyramid` application more or less
-unchanged. However, some specific template file owned by the
-application might have inappropriate HTML, or some static resource
-(such as a logo file or some CSS file) might not be appropriate. You
-*could* just fork the application entirely, but it's often more
-convenient to just override the resources that are inappropriate and
-reuse the application "as is". This is particularly true when you
-reuse some "core" application over and over again for some set of
-customers (such as a CMS application, or some bug tracking
-application), and you want to make arbitrary visual modifications to a
-particular application deployment without forking the underlying code.
+Resources can optionally be made to implement an :term:`interface`. An
+interface is used to tag a resource object with a "type" that can later be
+referred to within :term:`view configuration`.
-To this end, :app:`Pyramid` contains a feature that makes it
-possible to "override" one resource with one or more other resources.
-In support of this feature, a :term:`ZCML` directive exists named
-``resource``. The ``resource`` directive allows you to *override* the
-following kinds of resources defined in any Python package:
+Specifying an interface instead of a class as the ``context`` or
+``containment`` predicate arguments within :term:`view configuration`
+statements effectively 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.
-- Individual :term:`Chameleon` templates.
+For example, here's some code which describes a blog entry which also
+declares that the blog entry implements an :term:`interface`.
-- A directory containing multiple Chameleon templates.
+.. code-block:: python
+ :linenos:
-- Individual static files served up by an instance of the
- ``pyramid.view.static`` helper class.
+ import datetime
+ from zope.interface import implements
+ from zope.interface import Interface
-- A directory of static files served up by an instance of the
- ``pyramid.view.static`` helper class.
+ class IBlogEntry(Interface):
+ pass
-- Any other resource (or set of resources) addressed by code that uses
- the setuptools :term:`pkg_resources` API.
+ 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()
-.. index::
- single: override_resource
+This resource consists of two things: the class which defines the resource
+constructor as the class ``BlogEntry``, and an :term:`interface` attached to
+the class via an ``implements`` statement at class scope using the
+``IBlogEntry`` interface as its sole argument.
-.. _override_resource:
+The interface object used must be an instance of a class that inherits from
+:class:`zope.interface.Interface`.
-The ``override_resource`` API
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A resource class may implement zero or more interfaces. You specify that a
+resource implements an interface by using the
+:func:`zope.interface.implements` function at class scope. The above
+``BlogEntry`` resource implements the ``IBlogEntry`` interface.
-An individual call to
-:meth:`pyramid.config.Configurator.override_resource` can
-override a single resource. For example:
+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 :func:`zope.interface.directlyProvides` function:
-.. ignore-next-block
.. code-block:: python
:linenos:
- config.override_resource(
- to_override='some.package:templates/mytemplate.pt',
- override_with='another.package:othertemplates/anothertemplate.pt')
+ from zope.interface import directlyProvides
+ from zope.interface import Interface
-The string value passed to both ``to_override`` and ``override_with``
-attached to a resource directive is called a "specification". The
-colon separator in a specification separates the *package name* from
-the *resource name*. The colon and the following resource name are
-optional. If they are not specified, the override attempts to resolve
-every lookup into a package from the directory of another package.
-For example:
+ class IBlogEntry(Interface):
+ pass
-.. ignore-next-block
-.. code-block:: python
- :linenos:
+ class BlogEntry(object):
+ def __init__(self, title, body, author):
+ self.title = title
+ self.body = body
+ self.author = author
+ self.created = datetime.datetime.now()
- config.override_resource(to_override='some.package',
- override_with='another.package')
+ entry = BlogEntry('title', 'body', 'author')
+ directlyProvides(entry, IBlogEntry)
-Individual subdirectories within a package can also be overridden:
+:func:`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 :func:`zope.interface.alsoProvides` function:
-.. ignore-next-block
.. code-block:: python
:linenos:
- config.override_resource(to_override='some.package:templates/',
- override_with='another.package:othertemplates/')
-
+ from zope.interface import alsoProvides
+ from zope.interface import directlyProvides
+ from zope.interface import Interface
-If you wish to override a directory with another directory, you *must*
-make sure to attach the slash to the end of both the ``to_override``
-specification and the ``override_with`` specification. If you fail to
-attach a slash to the end of a specification that points to a directory,
-you will get unexpected results.
+ class IBlogEntry1(Interface):
+ pass
-You cannot override a directory specification with a file
-specification, and vice versa: a startup error will occur if you try.
-You cannot override a resource with itself: a startup error will occur
-if you try.
+ class IBlogEntry2(Interface):
+ pass
-Only individual *package* resources may be overridden. Overrides will
-not traverse through subpackages within an overridden package. This
-means that if you want to override resources for both
-``some.package:templates``, and ``some.package.views:templates``, you
-will need to register two overrides.
-
-The package name in a specification may start with a dot, meaning that
-the package is relative to the package in which the configuration
-construction file resides (or the ``package`` argument to the
-:class:`pyramid.config.Configurator` class construction).
-For example:
-
-.. ignore-next-block
-.. code-block:: python
- :linenos:
+ class BlogEntry(object):
+ def __init__(self, title, body, author):
+ self.title = title
+ self.body = body
+ self.author = author
+ self.created = datetime.datetime.now()
- config.override_resource(to_override='.subpackage:templates/',
- override_with='another.package:templates/')
+ entry = BlogEntry('title', 'body', 'author')
+ directlyProvides(entry, IBlogEntry1)
+ alsoProvides(entry, IBlogEntry2)
-Multiple ``override_resource`` statements which name a shared
-``to_override`` but a different ``override_with`` specification can be
-"stacked" to form a search path. The first resource that exists in
-the search path will be used; if no resource exists in the override
-path, the original resource is used.
+:func:`zope.interface.alsoProvides` will augment the set of interfaces
+directly provided by an instance instead of overwriting them like
+:func:`zope.interface.directlyProvides` does.
-Resource overrides can actually override resources other than
-templates and static files. Any software which uses the
-:func:`pkg_resources.get_resource_filename`,
-:func:`pkg_resources.get_resource_stream` or
-:func:`pkg_resources.get_resource_string` APIs will obtain an
-overridden file when an override is used.
+For more information about how resource interfaces can be used by view
+configuration, see :ref:`using_resource_interfaces`.
.. index::
- pair: ZCML directive; resource
+ single: resource tree
+ single: traversal tree
+ single: object tree
+ single: container resources
+ single: leaf resources
+
+Defining a Resource Tree
+------------------------
+
+When :term:`traversal` is used (as opposed to a purely :term:`url dispatch`
+based application), :app:`Pyramid` expects to be able to traverse a tree
+composed of resources (the :term:`resource tree`). Traversal begins at a
+root resource, and descends into the tree recursively via each resource's
+``__getitem__`` method. :app:`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 a :exc:`KeyError`. 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 raise a
+ :exc:`KeyError`.
+
+See :ref:`traversal_chapter` for more information about how traversal
+works against resource instances.
-.. _resource_zcml_directive:
+.. index::
+ pair: location-aware; resource
-The ``resource`` ZCML Directive
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. _location_aware:
-Instead of using
-:meth:`pyramid.config.Configurator.override_resource` during
-:term:`imperative configuration`, an equivalent can be used to perform
-all the tasks described above within :term:`ZCML`. The ZCML
-``resource`` tag is a frontend to using ``override_resource``.
+Location-Aware Resources
+------------------------
-An individual :app:`Pyramid` ``resource`` ZCML statement can
-override a single resource. For example:
+Applications which use :term:`traversal` to locate the :term:`context`
+resource of a view must ensure that the resources that make up the
+resource tree are "location aware".
-.. code-block:: xml
- :linenos:
+In order for :app:`Pyramid` location, security, URL-generation, and traversal
+functions (i.e., functions in :ref:`location_module`,
+:ref:`traversal_module`, :ref:`url_module` and some in :ref:`security_module`
+) to work properly against the resources in a resource tree, all resources in
+the tree must be :term:`location` -aware. This means they must have two
+attributes: ``__parent__`` and ``__name__``.
- <resource
- to_override="some.package:templates/mytemplate.pt"
- override_with="another.package:othertemplates/anothertemplate.pt"
- />
+The ``__parent__`` attribute 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 string value passed to both ``to_override`` and ``override_with``
-attached to a resource directive is called a "specification". The
-colon separator in a specification separates the *package name* from
-the *resource name*. The colon and the following resource name are
-optional. If they are not specified, the override attempts to resolve
-every lookup into a package from the directory of another package.
-For example:
+The ``__parent__`` of the root resource should be ``None`` and its
+``__name__`` should be the empty string. For instance:
-.. code-block:: xml
+.. code-block:: python
:linenos:
- <resource
- to_override="some.package"
- override_with="another.package"
- />
-
-Individual subdirectories within a package can also be overridden:
+ 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
+"upward" 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
+ :func:`pyramid.url.resource_url` function and paths generated by
+ the :func:`pyramid.traversal.resource_path` and
+ :func:`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).
+
+.. sidebar:: Using :mod:`pyramid_traversalwrapper`
+
+ If you'd rather not manage the ``__name__`` and ``__parent__`` attributes
+ of your resources "by hand", an add-on package named
+ :mod:`pyramid_traversalwrapper` can help.
+
+ In order to use this helper feature, you must first install the
+ :mod:`pyramid_traversalwrapper` package (available via PyPI), then register
+ its ``ModelGraphTraverser`` as the traversal policy, rather than the
+ default :app:`Pyramid` traverser. The package contains instructions for
+ doing so.
+
+ Once :app:`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 :app:`Pyramid`
+ will wrap each resource (even the root resource) in a ``LocationProxy``
+ which will dynamically assign a ``__name__`` and a ``__parent__`` to the
+ traversed resrouce (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``.
-.. code-block:: xml
- :linenos:
+.. index::
+ single: resource API functions
+ single: url generation (traversal)
- <resource
- to_override="some.package:templates/"
- override_with="another.package:othertemplates/"
- />
+:app:`Pyramid` API Functions That Act Against Resources
+-------------------------------------------------------
-If you wish to override a directory with another directory, you *must*
-make sure to attach the slash to the end of both the ``to_override``
-specification and the ``override_with`` specification. If you fail to
-attach a slash to the end of a specification that points to a directory,
-you will get unexpected results.
+A resource object is used as the :term:`context` provided to a view. See
+:ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information
+about how a resource object becomes the context.
-The package name in a specification may start with a dot, meaning that
-the package is relative to the package in which the ZCML file resides.
-For example:
+The APIs provided by :ref:`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.
-.. code-block:: xml
- :linenos:
+The APIs provided by :ref:`location_module` are used against resources.
+These can be used to walk down a resource tree, or conveniently locate one
+resource "inside" another.
- <resource
- to_override=".subpackage:templates/"
- override_with="another.package:templates/"
- />
+Some APIs in :ref:`security_module` accept a resource object as a parameter.
+For example, the :func:`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 :mod:`pyramid.security`
+module also accept :term:`context` as an argument, and a context is always a
+resource.
-See also :ref:`resource_directive`.