diff options
| author | Chris McDonough <chrism@plope.com> | 2010-12-18 02:27:14 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2010-12-18 02:27:14 -0500 |
| commit | fb6a5ce52a275f7798e82a34b5907ea118cbd2ff (patch) | |
| tree | 29e80d0eb681676d1e0eb2707ca764dbf5491864 /docs/narr | |
| parent | bf89764a7e3ccab6133c9ad43b8d9af4f5c4083b (diff) | |
| download | pyramid-fb6a5ce52a275f7798e82a34b5907ea118cbd2ff.tar.gz pyramid-fb6a5ce52a275f7798e82a34b5907ea118cbd2ff.tar.bz2 pyramid-fb6a5ce52a275f7798e82a34b5907ea118cbd2ff.zip | |
model -> resource; resource -> asset
Diffstat (limited to 'docs/narr')
| -rw-r--r-- | docs/narr/assets.rst | 198 | ||||
| -rw-r--r-- | docs/narr/contextfinding.rst | 18 | ||||
| -rw-r--r-- | docs/narr/declarative.rst | 101 | ||||
| -rw-r--r-- | docs/narr/extending.rst | 10 | ||||
| -rw-r--r-- | docs/narr/hooks.rst | 42 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 51 | ||||
| -rw-r--r-- | docs/narr/introduction.rst | 24 | ||||
| -rw-r--r-- | docs/narr/models.rst | 331 | ||||
| -rw-r--r-- | docs/narr/project.rst | 8 | ||||
| -rw-r--r-- | docs/narr/resources.rst | 465 | ||||
| -rw-r--r-- | docs/narr/resourcetreetraverser.png (renamed from docs/narr/modelgraphtraverser.png) | bin | 232233 -> 232233 bytes | |||
| -rw-r--r-- | docs/narr/security.rst | 67 | ||||
| -rw-r--r-- | docs/narr/static.rst | 197 | ||||
| -rw-r--r-- | docs/narr/templates.rst | 29 | ||||
| -rw-r--r-- | docs/narr/threadlocals.rst | 8 | ||||
| -rw-r--r-- | docs/narr/traversal.rst | 199 | ||||
| -rw-r--r-- | docs/narr/unittesting.rst | 37 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 12 | ||||
| -rw-r--r-- | docs/narr/vhosting.rst | 28 | ||||
| -rw-r--r-- | docs/narr/views.rst | 289 | ||||
| -rw-r--r-- | docs/narr/zca.rst | 2 |
21 files changed, 1024 insertions, 1092 deletions
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst new file mode 100644 index 000000000..1932e19ff --- /dev/null +++ b/docs/narr/assets.rst @@ -0,0 +1,198 @@ +.. index:: + single: assets + +.. _assets_chapter: + +Assets +====== + +An :term:`asset` is any file contained within a Python :term:`package` which +is *not* a Python source code file. For example, each of the following is an +asset: + +- 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). + +The use of assets 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 +assets. For example, there is a ``templates`` directory which contains +``.pt`` files, and a ``static`` directory which contains ``.css``, ``.js``, +and ``.gif`` files. + +.. _understanding_assets: + +Understanding Assets +-------------------- + +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 asset named ``templates/some_template.pt`` +using that API within a ``views.py`` file inside a ``myapp`` package: + +.. 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``) + +- The *asset name* (``templates/some_template.pt``) + +:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the package name +and asset 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. +Asset names usually look a lot like relative UNIX file paths. + +.. index:: + pair: overriding; assets + +.. _overriding_assets_section: + +Overriding Assets +----------------- + +It can often be useful to override specific assets 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 asset (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 assets 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. + +To this end, :app:`Pyramid` contains a feature that makes it possible to +"override" one asset with one or more other assets. In support of this +feature, a :term:`Configurator` API exists named +:meth:`pyramid.config.Configurator.override_asset`. This API allows you to +*override* the following kinds of assets defined in any Python package: + +- Individual :term:`Chameleon` templates. + +- A directory containing multiple Chameleon templates. + +- Individual static files served up by an instance of the + ``pyramid.view.static`` helper class. + +- A directory of static files served up by an instance of the + ``pyramid.view.static`` helper class. + +- Any other asset (or set of assets) addressed by code that uses the + setuptools :term:`pkg_resources` API. + +.. note:: The :term:`ZCML` directive named ``asset`` serves the same purpose + as the :meth:`pyramid.config.Configurator.override_asset` method. + +.. index:: + single: override_asset + +.. _override_asset: + +The ``override_asset`` API +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An individual call to :meth:`pyramid.config.Configurator.override_asset` +can override a single asset. For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset( + to_override='some.package:templates/mytemplate.pt', + override_with='another.package:othertemplates/anothertemplate.pt') + +The string value passed to both ``to_override`` and ``override_with`` sent to +the ``override_asset`` API is called an :term:`asset specification`. The +colon separator in a specification separates the *package name* from the +*asset name*. The colon and the following asset 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: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='some.package', + override_with='another.package') + +Individual subdirectories within a package can also be overridden: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='some.package:templates/', + override_with='another.package:othertemplates/') + + +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. + +You cannot override a directory specification with a file specification, and +vice versa: a startup error will occur if you try. You cannot override an +asset with itself: a startup error will occur if you try. + +Only individual *package* assets may be overridden. Overrides will not +traverse through subpackages within an overridden package. This means that +if you want to override assets 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: + + config.override_asset(to_override='.subpackage:templates/', + override_with='another.package:templates/') + +Multiple calls to ``override_asset`` which name a shared ``to_override`` but +a different ``override_with`` specification can be "stacked" to form a search +path. The first asset that exists in the search path will be used; if no +asset exists in the override path, the original asset is used. + +Asset overrides can actually override assets 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. + diff --git a/docs/narr/contextfinding.rst b/docs/narr/contextfinding.rst index 691ad7b8e..ad0d1f9da 100644 --- a/docs/narr/contextfinding.rst +++ b/docs/narr/contextfinding.rst @@ -95,20 +95,20 @@ don't deal very well with URLs that represent arbitrary-depth hierarchies. But :term:`traversal` *does* work well for URLs that represent -arbitrary-depth hierarchies. Since the path segments that compose a -URL are addressed separately, it becomes very easy to form URLs that -represent arbitrary depth hierarchies in a system that uses traversal. -When you're willing to treat your application models as a graph that -can be traversed, it also becomes easy to provide "instance-level -security": you just attach a security declaration to each instance in -the graph. This is not nearly as easy to do when using URL dispatch. +arbitrary-depth hierarchies. Since the path segments that compose a URL are +addressed separately, it becomes very easy to form URLs that represent +arbitrary depth hierarchies in a system that uses traversal. When you're +willing to treat your application resources as a tree that can be traversed, +it also becomes easy to provide "instance-level security": you just attach a +security declaration to each instance in the tree. This is not nearly as +easy to do when using URL dispatch. In essence, the choice to use traversal vs. URL dispatch is largely religious. Traversal dispatch probably just doesn't make any sense when you possess completely "square" data stored in a relational database because it requires the construction and maintenance of a -graph and requires that the developer think about mapping URLs to code -in terms of traversing that graph. However, when you have a +tree and requires that the developer think about mapping URLs to code +in terms of traversing that tree. However, when you have a hierarchical data store, using traversal can provide significant advantages over using URL-based dispatch. diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst index 99c5a75ac..54e9ecaa1 100644 --- a/docs/narr/declarative.rst +++ b/docs/narr/declarative.rst @@ -557,7 +557,7 @@ example of a view declaration in ZCML is as follows: :linenos: <view - context=".models.Hello" + context=".resources.Hello" view=".views.hello_world" name="hello.html" /> @@ -566,7 +566,7 @@ The above maps the ``.views.hello_world`` view callable function to the following set of :term:`context finding` results: - A :term:`context` object which is an instance (or subclass) of the - Python class represented by ``.models.Hello`` + Python class represented by ``.resources.Hello`` - A :term:`view name` equalling ``hello.html``. @@ -575,28 +575,27 @@ the following set of :term:`context finding` results: above) mean "relative to the Python package directory in which this :term:`ZCML` file is stored". So if the above ``view`` declaration was made inside a ``configure.zcml`` file that lived in the - ``hello`` package, you could replace the relative ``.models.Hello`` - with the absolute ``hello.models.Hello``; likewise you could + ``hello`` package, you could replace the relative ``.resources.Hello`` + with the absolute ``hello.resources.Hello``; likewise you could replace the relative ``.views.hello_world`` with the absolute ``hello.views.hello_world``. Either the relative or absolute form is functionally equivalent. It's often useful to use the relative form, in case your package's name changes. It's also shorter to type. -You can also declare a *default view callable* for a :term:`model` -type: +You can also declare a *default view callable* for a :term:`resource` type: .. code-block:: xml :linenos: <view - context=".models.Hello" + context=".resources.Hello" view=".views.hello_world" /> A *default view callable* simply has no ``name`` attribute. For the above registration, when a :term:`context` is found that is of the -type ``.models.Hello`` and there is no :term:`view name` associated +type ``.resources.Hello`` and there is no :term:`view name` associated with the result of :term:`context finding`, the *default view callable* will be used. In this case, it's the view at ``.views.hello_world``. @@ -608,7 +607,7 @@ string as its ``name`` attribute: :linenos: <view - context=".models.Hello" + context=".resources.Hello" view=".views.hello_world" name="" /> @@ -823,6 +822,75 @@ an imperative equivalent to the ``static`` ZCML directive. Use of the to using ZCML for the same purpose. See :ref:`static_resources_section` for more information. +.. index:: + pair: ZCML directive; asset + +.. _asset_zcml_directive: + +The ``asset`` ZCML Directive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of using :meth:`pyramid.config.Configurator.override_asset` during +:term:`imperative configuration`, an equivalent ZCML directive can be used. +The ZCML ``asset`` tag is a frontend to using +:meth:`pyramid.config.Configurator.override_asset`. + +An individual :app:`Pyramid` ``asset`` ZCML statement can override a +single asset. For example: + +.. code-block:: xml + :linenos: + + <asset + to_override="some.package:templates/mytemplate.pt" + override_with="another.package:othertemplates/anothertemplate.pt" + /> + +The string value passed to both ``to_override`` and ``override_with`` +attached to an ``asset`` directive is called an "asset specification". The +colon separator in a specification separates the *package name* from the +*asset name*. The colon and the following asset 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: + +.. code-block:: xml + :linenos: + + <asset + to_override="some.package" + override_with="another.package" + /> + +Individual subdirectories within a package can also be overridden: + +.. code-block:: xml + :linenos: + + <asset + to_override="some.package:templates/" + override_with="another.package:othertemplates/" + /> + +If you wish to override an asset 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 an asset specification that points to a directory, you +will get unexpected results. + +The package name in an asset specification may start with a dot, meaning that +the package is relative to the package in which the ZCML file resides. For +example: + +.. code-block:: xml + :linenos: + + <asset + to_override=".subpackage:templates/" + override_with="another.package:templates/" + /> + +See also :ref:`asset_directive`. + .. _zcml_authorization_policy: Enabling an Authorization Policy Via ZCML @@ -833,12 +901,11 @@ than imperative configuration, modify the ZCML file loaded by your application (usually named ``configure.zcml``) to enable an authorization policy. -For example, to enable a policy which compares the value of an "auth -ticket" cookie passed in the request's environment which contains a -reference to a single :term:`principal` against the principals present -in any :term:`ACL` found in model data when attempting to call some -:term:`view`, modify your ``configure.zcml`` to look something like -this: +For example, to enable a policy which compares the value of an "auth ticket" +cookie passed in the request's environment which contains a reference to a +single :term:`principal` against the principals present in any :term:`ACL` +found in the resource tree when attempting to call some :term:`view`, modify +your ``configure.zcml`` to look something like this: .. code-block:: xml :linenos: @@ -958,7 +1025,7 @@ Built-In Authorization Policy ZCML Directives ``aclauthorizationpolicy`` When this directive is used, authorization information is obtained -from :term:`ACL` objects attached to model instances. +from :term:`ACL` objects attached to resources. An example of its usage, with all attributes fully expanded: @@ -1069,7 +1136,6 @@ with ``.jinja2`` as its ``renderer`` value. The ``name`` passed to the See also :ref:`renderer_directive` and :meth:`pyramid.config.Configurator.add_renderer`. - Overriding an Existing Renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1207,4 +1273,3 @@ See also :ref:`subscriber_directive` and :ref:`events_chapter`. .. - hooks chapter still has topics for ZCML -.. - resources chapter still has topics for ZCML diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 1f9fd1288..9802a01f6 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -191,15 +191,15 @@ the original application with slight tweaks. For example: :linenos: <view - context="theoriginalapplication.models.SomeModel" + context="theoriginalapplication.resources.SomeResource" name="theview" view=".views.a_view_that_does_something_slightly_different" /> -A similar pattern can be used to *extend* the application with -``<view>`` declarations. Just register a new view against some -existing model type and make sure the URLs it implies are available on -some other page rendering. +A similar pattern can be used to *extend* the application with ``<view>`` +declarations. Just register a new view against some existing resource type +(using ``context``) and make sure the URLs it implies are available on some +other page rendering. .. index:: pair: overriding; routes diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 10f463a17..006f5d5cb 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -88,11 +88,10 @@ callable: it is false. .. warning:: When a NotFound view callable accepts an argument list as - described in :ref:`request_and_context_view_definitions`, the - ``context`` passed as the first argument to the view callable will - be the :exc:`pyramid.exceptions.NotFound` exception instance. - If available, the *model* context will still be available as - ``request.context``. + described in :ref:`request_and_context_view_definitions`, the ``context`` + passed as the first argument to the view callable will be the + :exc:`pyramid.exceptions.NotFound` exception instance. If available, the + resource context will still be available as ``request.context``. .. index:: single: forbidden view @@ -221,14 +220,14 @@ a class that implements the following interface: """ Return a dictionary with (at least) the keys ``root``, ``context``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. These values are - typically the result of an object graph traversal. ``root`` - is the physical root object, ``context`` will be a model + typically the result of a resource tree traversal. ``root`` + is the physical root object, ``context`` will be a resource object, ``view_name`` will be the view name used (a Unicode name), ``subpath`` will be a sequence of Unicode names that followed the view name but were not traversed, ``traversed`` will be a sequence of Unicode names that were traversed (including the virtual root path, if any) ``virtual_root`` - will be a model object representing the virtual root (or the + will be a resource object representing the virtual root (or the physical root if traversal was not performed), and ``virtual_root_path`` will be a sequence representing the virtual root path (a sequence of Unicode names) or None if @@ -255,30 +254,29 @@ traverser would be used. For example: <adapter factory="myapp.traversal.Traverser" provides="pyramid.interfaces.ITraverser" - for="myapp.models.MyRoot" + for="myapp.resources.MyRoot" /> If the above stanza was added to a ``configure.zcml`` file, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only when the application :term:`root factory` returned an instance of the -``myapp.models.MyRoot`` object. Otherwise it would use the default +``myapp.resources.MyRoot`` object. Otherwise it would use the default :app:`Pyramid` traverser to do traversal. .. index:: single: url generator -Changing How :mod:`pyramid.url.model_url` Generates a URL +Changing How :mod:`pyramid.url.resource_url` Generates a URL ------------------------------------------------------------ -When you add a traverser as described in -:ref:`changing_the_traverser`, it's often convenient to continue to -use the :func:`pyramid.url.model_url` API. However, since the way -traversal is done will have been modified, the URLs it generates by -default may be incorrect. +When you add a traverser as described in :ref:`changing_the_traverser`, it's +often convenient to continue to use the :func:`pyramid.url.resource_url` API. +However, since the way traversal is done will have been modified, the URLs it +generates by default may be incorrect. If you've added a traverser, you can change how -:func:`pyramid.url.model_url` generates a URL for a specific type -of :term:`context` by adding an adapter stanza for +:func:`pyramid.url.resource_url` generates a URL for a specific type of +resource by adding an adapter stanza for :class:`pyramid.interfaces.IContextURL` to your application's ``configure.zcml``: @@ -288,13 +286,13 @@ of :term:`context` by adding an adapter stanza for <adapter factory="myapp.traversal.URLGenerator" provides="pyramid.interfaces.IContextURL" - for="myapp.models.MyRoot *" + for="myapp.resources.MyRoot *" /> In the above example, the ``myapp.traversal.URLGenerator`` class will -be used to provide services to :func:`pyramid.url.model_url` any -time the :term:`context` passed to ``model_url`` is of class -``myapp.models.MyRoot``. The asterisk following represents the type +be used to provide services to :func:`pyramid.url.resource_url` any +time the :term:`context` passed to ``resource_url`` is of class +``myapp.resources.MyRoot``. The asterisk following represents the type of interface that must be possessed by the :term:`request` (in this case, any interface, represented by asterisk). diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index da77a28f0..f9dab9ff6 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -171,7 +171,7 @@ match is straightforward. When a route is matched: Root factories related to a route were explained previously within :ref:`route_factories`. Both the global root factory and default root factory were explained previously within - :ref:`the_object_graph`. + :ref:`the_resource_tree`. .. _using_traverse_in_a_route_pattern: @@ -226,7 +226,7 @@ we've created a root factory that looks like so in a module named .. code-block:: python :linenos: - class Traversable(object): + class Resource(object): def __init__(self, subobjects): self.subobjects = subobjects @@ -234,15 +234,15 @@ we've created a root factory that looks like so in a module named return self.subobjects[name] root = Traversable( - {'a':Traversable({'b':Traversable({'c':Traversable({})})})} + {'a':Resource({'b':Resource({'c':Resource({})})})} ) def root_factory(request): return root -Above, we've defined a (bogus) graph that can be traversed, and a -``root_factory`` function that can be used as part of a particular -route configuration statement: +Above, we've defined a (bogus) resource tree that can be traversed, and a +``root_factory`` function that can be used as part of a particular route +configuration statement: .. code-block:: python :linenos: @@ -250,14 +250,13 @@ route configuration statement: config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') -The ``factory`` above points at the function we've defined. It will -return an instance of the ``Traversable`` class as a root object -whenever this route is matched. Instances of the``Traversable`` class -can be used for graph traversal because they have a ``__getitem__`` -method that does something nominally useful. Since traversal uses -``__getitem__`` to walk the nodes of an object graph, using traversal -against the root object implied by our route statement is a reasonable -thing to do. +The ``factory`` above points at the function we've defined. It will return +an instance of the ``Traversable`` class as a root object whenever this route +is matched. Instances of the``Resource`` class can be used for tree +traversal because they have a ``__getitem__`` method that does something +nominally useful. Since traversal uses ``__getitem__`` to walk the resources +of a resource tree, using traversal against the root resource implied by our +route statement is a reasonable thing to do. .. note:: @@ -271,12 +270,11 @@ thing to do. When the route configuration named ``home`` above is matched during a request, the matchdict generated will be based on its pattern: -``{foo}/{bar}/*traverse``. The "capture value" implied by the -``*traverse`` element in the pattern will be used to traverse the -graph in order to find a context, starting from the root object -returned from the root factory. In the above example, the -:term:`root` object found will be the instance named ``root`` in -``routes.py``. +``{foo}/{bar}/*traverse``. The "capture value" implied by the ``*traverse`` +element in the pattern will be used to traverse the resource tree in order to +find a context, starting from the root object returned from the root factory. +In the above example, the :term:`root` object found will be the instance +named ``root`` in ``routes.py``. If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``, is ``http://example.com/one/two/a/b/c``, the traversal path used @@ -284,12 +282,11 @@ against the root object will be ``a/b/c``. As a result, :app:`Pyramid` will attempt to traverse through the edges ``a``, ``b``, and ``c``, beginning at the root object. -In our above example, this particular set of traversal steps will mean -that the :term:`context` of the view would be the ``Traversable`` -object we've named ``c`` in our bogus graph and the :term:`view name` -resulting from traversal will be the empty string; if you need a -refresher about why this outcome is presumed, see -:ref:`traversal_algorithm`. +In our above example, this particular set of traversal steps will mean that +the :term:`context` of the view would be the ``Traversable`` object we've +named ``c`` in our bogus resource tree and the :term:`view name` resulting +from traversal will be the empty string; if you need a refresher about why +this outcome is presumed, see :ref:`traversal_algorithm`. At this point, a suitable view callable will be found and invoked using :term:`view lookup` as described in :ref:`view_configuration`, @@ -347,7 +344,7 @@ above ``mypackage.views.another_view`` view will be invoked when: - the :term:`context` is any object. For instance, if the URL ``http://example.com/one/two/a/another`` is provided -to an application that uses the previously mentioned object graph, the +to an application that uses the previously mentioned resource tree, the ``mypackage.views.another`` view callable will be called instead of the ``mypackage.views.myview`` view callable because the :term:`view name` will be ``another`` instead of the empty string. diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 7c725690d..0d8077fc4 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -144,10 +144,9 @@ it only supplies a mechanism to map URLs to :term:`view` code, along with a set of conventions for calling those views. You are free to use third-party components that fit your needs in your applications. -The concepts of :term:`view` and :term:`model` are used by -:app:`Pyramid` mostly as they would be by Django. -:app:`Pyramid` has a documentation culture more like Django's than -like Zope's. +The concept of :term:`view` is used by :app:`Pyramid` mostly as it would be +by Django. :app:`Pyramid` has a documentation culture more like Django's +than like Zope's. Like :term:`Pylons` version 1.0, but unlike :term:`Zope`, a :app:`Pyramid` application developer may use completely imperative @@ -177,13 +176,12 @@ frameworks, :app:`Pyramid` also generally fits into this class. .. sidebar:: You Say :app:`Pyramid` is MVC, But Where's The Controller? - The :app:`Pyramid` authors believe that the MVC pattern just - doesn't really fit the web very well. In a :app:`Pyramid` - application, there are models, which store data, and views, which - present the data stored in models. However, no facility provided - by the framework actually maps to the concept of a "controller". - So :app:`Pyramid` is actually an "MV" framework rather than an - "MVC" framework. "MVC", however, is close enough as a general - classification moniker for purposes of comparison with other web - frameworks. + The :app:`Pyramid` authors believe that the MVC pattern just doesn't + really fit the web very well. In a :app:`Pyramid` application, there is a + model, which stores data, and views, which present the data stored in the + model. However, no facility provided by the framework actually maps to + the concept of a "controller". So :app:`Pyramid` is actually an "MV" + framework rather than an "MVC" framework. "MVC", however, is close enough + as a general classification moniker for purposes of comparison with other + web frameworks. diff --git a/docs/narr/models.rst b/docs/narr/models.rst deleted file mode 100644 index 91828287f..000000000 --- a/docs/narr/models.rst +++ /dev/null @@ -1,331 +0,0 @@ -Models -====== - -A :term:`model` class is typically a simple Python class defined in a module. -References to these classes and instances of such classes are omnipresent in -:app:`Pyramid` when :term:`traversal` is used to build an application: - -- Model instances make up the object graph that :app:`Pyramid` - will walk over when :term:`traversal` is used. - -- The ``context`` and ``containment`` arguments to - :meth:`pyramid.config.Configurator.add_view` often - reference a model class. - -- A :term:`root factory` returns a model instance. - -- A model instance is exposed to :term:`view` code as the :term:`context` of - a view. - -In many :term:`traversal` based applications (particularly ones that use -Zope-like patterns), model objects often store data and offer methods related -to mutating that data. - -.. note:: - - A terminology overlap confuses people who write applications that - always use ORM packages such as SQLAlchemy, which has a very - different notion of the definition of a "model". When using the API - of common ORM packages, its conception of "model" is almost - certainly not the same conception of "model" used by - :app:`Pyramid`. In particular, it can be unnatural to think of - :app:`Pyramid` model objects as "models" if you develop your - application using :term:`traversal` and a relational database. When - you develop such applications, the object graph *might* be composed - completely of "model" objects (as defined by the ORM) but it also - might not be. The things that :app:`Pyramid` refers to as - "models" in such an application may instead just be stand-ins that - perform a query and generate some wrapper *for* an ORM "model" or - set of ORM models. This naming overlap is slightly unfortunate. - However, many :app:`Pyramid` applications (especially ones which - use :term:`ZODB`) do indeed traverse a graph full of literal model - nodes. Each node in the graph is a separate persistent object that - is stored within a database. This was the use case considered when - coming up with the "model" terminology. However, if we had it to do - all over again, we'd probably call these objects something - different to avoid confusion. - -.. index:: - single: model constructor - -Defining a Model Constructor ----------------------------- - -An example of a model constructor, ``BlogEntry`` is presented below. -It is implemented as a class which, when instantiated, becomes a model -instance. - -.. code-block:: python - :linenos: - - import datetime - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - -A model constructor may be essentially any Python object which is -callable, and which returns a model instance. In the above example, -the ``BlogEntry`` class can be called, returning a model instance. - -.. index:: - single: model interfaces - -.. _models_which_implement_interfaces: - -Model Instances Which Implement Interfaces ------------------------------------------- - -Model instances can optionally be made to implement an -:term:`interface`. An interface is used to tag a model object with a -"type" that can later be referred to within :term:`view -configuration`. - -Specifying an interface instead of a class as the ``context`` or -``containment`` arguments within :term:`view configuration` statements -effectively makes it possible to use a single view callable for more -than one class of 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 :term:`interface`. - -.. code-block:: python - :linenos: - - 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 model consists of two things: the class which defines the model -constructor (above 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). - -The interface object used must be an instance of a class that inherits -from :class:`zope.interface.Interface`. - -A model class may *implement* zero or more interfaces. You specify -that a model implements an interface by using the -:func:`zope.interface.implements` function at class scope. The above -``BlogEntry`` model implements the ``IBlogEntry`` interface. - -You can also specify that a *particular* model instance provides an -interface, as opposed to its class as above, which implies that all -instances do. To do so, use the :func:`zope.interface.directlyProvides` -function: - -.. code-block:: python - :linenos: - - 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) - -:func:`zope.interface.directlyProvides` will replace any existing -interface that was previously provided by an instance. If a model -object already has instance-level interface declarations that you -don't want to replace, use the :func:`zope.interface.alsoProvides` -function: - -.. code-block:: python - :linenos: - - 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) - -: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. - -For more information about how model interfaces can be used by view -configuration, see :ref:`using_model_interfaces`. - -.. index:: - single: model graph - single: traversal graph - single: object graph - single: container nodes - single: leaf nodes - -Defining a Graph of Model Instances for Traversal -------------------------------------------------- - -When :term:`traversal` is used (as opposed to a purely :term:`url -dispatch` based application), :app:`Pyramid` expects to be able to -traverse a graph composed of model instances. Traversal begins at a -root model instance, and descends into the graph recursively via each -found model's ``__getitem__`` method. :app:`Pyramid` imposes the -following policy on model instance nodes in the graph: - -- Container nodes (i.e., nodes which contain other nodes) must supply - a ``__getitem__`` method which is willing to resolve a unicode name - to a subobject. If a subobject by that name does not exist in the - container, ``__getitem__`` must raise a :exc:`KeyError`. If a - subobject by that name *does* exist, the container should return the - subobject (another model instance). - -- Leaf nodes, which do not contain other nodes, 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 model instances. - -.. index:: - pair: location-aware; model - -.. _location_aware: - -Location-Aware Model Instances ------------------------------- - -Applications which use :term:`traversal` to locate the :term:`context` -of a view must ensure that the model instances that make up the model -graph are "location aware". - -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 model instances in -an object graph, all nodes in the graph must be :term:`location` -aware. -This means they must have two attributes: ``__parent__`` and -``__name__``. - -The ``__parent__`` attribute should be a reference to the node's -parent model instance in the graph. The ``__name__`` attribute should -be the name that a node's parent refers to the node via -``__getitem__``. - -The ``__parent__`` of the root object should be ``None`` and its -``__name__`` should be the empty string. For instance: - -.. code-block:: python - :linenos: - - class MyRootObject(object): - __name__ = '' - __parent__ = None - -A node returned from the root item's ``__getitem__`` method should have -a ``__parent__`` attribute that is a reference to the root object, and -its ``__name__`` attribute should match the name by which it is -reachable via the root object's ``__getitem__``. A container under the -root should have a ``__getitem__`` that returns objects 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 down the graph. - -The ``__parent__`` attributes of each node 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 node in the traversal graph, you will eventually -come to the root node, just like if you keep executing the -``cd ..`` filesystem command, eventually you will reach the -filesystem root directory. - -.. warning:: If your root model object has a ``__name__`` argument - that is not ``None`` or the empty string, URLs returned by the - :func:`pyramid.url.model_url` function and paths generated by - the :func:`pyramid.traversal.model_path` and - :func:`pyramid.traversal.model_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 models "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 graph objects "by hand". Instead, as necessary, during traversal - :app:`Pyramid` will wrap each object (even the root object) in a - ``LocationProxy`` which will dynamically assign a ``__name__`` and a - ``__parent__`` to the traversed object (based on the last traversed - object and the name supplied to ``__getitem__``). The root object - will have a ``__name__`` attribute of ``None`` and a ``__parent__`` - attribute of ``None``. - -.. index:: - single: model API functions - single: url generation (traversal) - -:app:`Pyramid` API Functions That Act Against Models -------------------------------------------------------- - -A model instance is used as the :term:`context` provided to a view. See -:ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information -about how a model instance becomes the context. - -The APIs provided by :ref:`traversal_module` are used against model -instances. These functions can be used to find the "path" of a model, -the root model in an object graph, or generate a URL to a model. - -The APIs provided by :ref:`location_module` are used against model -instances. These can be used to walk down an object graph, or -conveniently locate one object "inside" another. - -Some APIs in :ref:`security_module` accept a model object as a -parameter. For example, the -:func:`pyramid.security.has_permission` API accepts a "context" (a -model object) as one of its arguments; the ACL is obtained from this -model 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 model. - diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 2b03f7373..65ef3c526 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -258,8 +258,8 @@ The Interactive Shell Once you've installed your program for development using ``setup.py develop``, you can use an interactive Python shell to examine your -:app:`Pyramid` application :term:`model` and :term:`view` objects from -a Python prompt. To do so, use the ``paster`` shell command with the +:app:`Pyramid` application :term:`resource` and :term:`view` objects from a +Python prompt. To do so, use the ``paster`` shell command with the ``pshell`` argument: The first argument to ``pshell`` is the path to your application's ``.ini`` @@ -290,7 +290,7 @@ the name ``MyProject`` as a section name: Type "help" for more information. "root" is the Pyramid app root object, "registry" is the Pyramid registry object. >>> root - <myproject.models.MyModel object at 0x445270> + <myproject.resources.MyResource object at 0x445270> >>> registry <Registry myproject> >>> registry.settings['debug_notfound'] @@ -868,7 +868,7 @@ named ``MyModel`` that provides the behavior. #. Line 6 is a "root factory" function that will be called by the :app:`Pyramid` *Router* for each request when it wants to find - the root of the object graph. Conventionally this is called + the root of the resource tree. Conventionally this is called ``get_root``. In a "real" application, the root object would not be such a simple 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`. diff --git a/docs/narr/modelgraphtraverser.png b/docs/narr/resourcetreetraverser.png Binary files differindex 037d12b18..037d12b18 100644 --- a/docs/narr/modelgraphtraverser.png +++ b/docs/narr/resourcetreetraverser.png diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 5edbc3ec3..469d3d298 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -92,11 +92,11 @@ For example: Python name` values, each representing the dotted name path to a suitable implementation global defined at Python module scope. -The above configuration enables a policy which compares the value of -an "auth ticket" cookie passed in the request's environment which -contains a reference to a single :term:`principal` against the -principals present in any :term:`ACL` found in model data when -attempting to call some :term:`view`. +The above configuration enables a policy which compares the value of an "auth +ticket" cookie passed in the request's environment which contains a reference +to a single :term:`principal` against the principals present in any +:term:`ACL` found in the resource tree when attempting to call some +:term:`view`. While it is possible to mix and match different authentication and authorization policies, it is an error to pass an authentication @@ -136,7 +136,7 @@ permission using the :meth:`pyramid.config.Configurator.add_view` API: config.add_view('mypackage.views.blog_entry_add_view', name='add_entry.html', - context='mypackage.models.Blog', + context='mypackage.resources.Blog', permission='add') The equivalent view registration including the ``add`` permission name @@ -147,7 +147,7 @@ may be performed via the ``@view_config`` decorator: :linenos: from pyramid.view import view_config - from models import Blog + from resources import Blog @view_config(context=Blog, name='add_entry.html', permission='add') def blog_entry_add_view(request): @@ -208,19 +208,18 @@ When a default permission is registered: .. _assigning_acls: -Assigning ACLs to your Model Objects ------------------------------------- +Assigning ACLs to your Resource Objects +--------------------------------------- -When the default :app:`Pyramid` :term:`authorization policy` -determines whether a user possesses a particular permission in a -:term:`context`, it examines the :term:`ACL` associated with the -context. An ACL is associated with a context by virtue of the -``__acl__`` attribute of the model object representing the -:term:`context`. This attribute can be defined on the model -*instance* if you need instance-level security, or it can be defined -on the model *class* if you just need type-level security. +When the default :app:`Pyramid` :term:`authorization policy` determines +whether a user possesses a particular permission in a :term:`context`, it +examines the :term:`ACL` associated with the context. An ACL is associated +with a context by virtue of the ``__acl__`` attribute of the resource object +representing the :term:`context`. This attribute can be defined on the +resource *instance* if you need instance-level security, or it can be defined +on the resource *class* if you just need type-level security. -For example, an ACL might be attached to the model for a blog via its +For example, an ACL might be attached to the resource for a blog via its class: .. code-block:: python @@ -236,8 +235,8 @@ class: (Allow, 'group:editors', 'edit'), ] -Or, if your models are persistent, an ACL might be specified via the -``__acl__`` attribute of an *instance* of a model: +Or, if your resources are persistent, an ACL might be specified via the +``__acl__`` attribute of an *instance* of a resource: .. code-block:: python :linenos: @@ -256,11 +255,11 @@ Or, if your models are persistent, an ACL might be specified via the (Allow, 'group:editors', 'edit'), ] -Whether an ACL is attached to a model's class or an instance of the -model itself, the effect is the same. It is useful to decorate -individual model instances with an ACL (as opposed to just decorating -their class) in applications such as "CMS" systems where fine-grained -access is required on an object-by-object basis. +Whether an ACL is attached to a resource's class or an instance of the +resource itself, the effect is the same. It is useful to decorate individual +resource instances with an ACL (as opposed to just decorating their class) in +applications such as "CMS" systems where fine-grained access is required on +an object-by-object basis. .. index:: single: ACE @@ -448,16 +447,16 @@ the following: ACL Inheritance and Location-Awareness -------------------------------------- -While the default :term:`authorization policy` is in place, if a model -object does not have an ACL when it is the context, its *parent* is -consulted for an ACL. If that object does not have an ACL, *its* -parent is consulted for an ACL, ad infinitum, until we've reached the -root and there are no more parents left. +While the default :term:`authorization policy` is in place, if a resource +object does not have an ACL when it is the context, its *parent* is consulted +for an ACL. If that object does not have an ACL, *its* parent is consulted +for an ACL, ad infinitum, until we've reached the root and there are no more +parents left. -In order to allow the security machinery to perform ACL inheritance, -model objects must provide *location-awareness*. Providing -*location-awareness* means two things: the root object in the graph -must have a ``_name__`` attribute and a ``__parent__`` attribute. +In order to allow the security machinery to perform ACL inheritance, resource +objects must provide *location-awareness*. Providing *location-awareness* +means two things: the root object in the resource tree must have a +``_name__`` attribute and a ``__parent__`` attribute. .. code-block:: python :linenos: diff --git a/docs/narr/static.rst b/docs/narr/static.rst index 172fb08d4..6b03b2349 100644 --- a/docs/narr/static.rst +++ b/docs/narr/static.rst @@ -1,26 +1,26 @@ -Static Resources -================ +Static Assets +============= -:app:`Pyramid` makes it possible to serve up static -resources files from a directory on a filesystem. This chapter describes -how to configure :app:`Pyramid` to do so. +:app:`Pyramid` makes it possible to serve up static asset files from a +directory on a filesystem. This chapter describes how to configure +:app:`Pyramid` to do so. .. index:: single: add_static_view -.. _static_resources_section: +.. _static_assets_section: -Serving Static Resources ------------------------- +Serving Static Assets +--------------------- -Use the :meth:`pyramid.config.Configurator.add_static_view` to -instruct :app:`Pyramid` to serve static resources such as JavaScript and CSS -files. This mechanism makes static files available at a name relative to the -application root URL, e.g. ``/static``. +Use the :meth:`pyramid.config.Configurator.add_static_view` to instruct +:app:`Pyramid` to serve static assets such as JavaScript and CSS files. This +mechanism makes static files available at a name relative to the application +root URL, e.g. ``/static``. Note that the ``path`` provided to -:meth:`pyramid.config.Configurator.add_static_view` may be a fully -qualified :term:`resource specification`, or an *absolute path*. +:meth:`pyramid.config.Configurator.add_static_view` may be a fully qualified +:term:`asset specification`, or an *absolute path*. Here's an example of a use of :meth:`pyramid.config.Configurator.add_static_view` that will serve @@ -34,11 +34,10 @@ path. # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='/var/www/static') -Here's an example of -:meth:`pyramid.config.Configurator.add_static_view` that will serve -files up under the ``/static`` URL from the ``a/b/c/static`` directory of the -Python package named ``some_package`` using a fully qualified :term:`resource -specification`. +Here's an example of :meth:`pyramid.config.Configurator.add_static_view` that +will serve files up under the ``/static`` URL from the ``a/b/c/static`` +directory of the Python package named ``some_package`` using a fully +qualified :term:`asset specification`. .. code-block:: python :linenos: @@ -46,7 +45,7 @@ specification`. # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='some_package:a/b/c/static') -Whether you use for ``path`` a fully qualified resource specification, or an +Whether you use for ``path`` a fully qualified asset specification, or an absolute path, when you place your static files on the filesystem in the directory represented as the ``path`` of the directive, you will then be able to view the static files in this directory via a browser at URLs prefixed @@ -58,13 +57,12 @@ subdirectories recursively, and any subdirectories may hold files; these will be resolved by the static view as you would expect. While the ``path`` argument can be a number of different things, the ``name`` -argument of the call to -:meth:`pyramid.config.Configurator.add_static_view` can also be one of -a number of things: a *view name* or a *URL*. The above examples have shown -usage of the ``name`` argument as a view name. When ``name`` is a *URL* (or -any string with a slash (``/``) in it), static resources can be served from -an external webserver. In this mode, the ``name`` is used as the URL prefix -when generating a URL using :func:`pyramid.url.static_url`. +argument of the call to :meth:`pyramid.config.Configurator.add_static_view` +can also be one of a number of things: a *view name* or a *URL*. The above +examples have shown usage of the ``name`` argument as a view name. When +``name`` is a *URL* (or any string with a slash (``/``) in it), static assets +can be served from an external webserver. In this mode, the ``name`` is used +as the URL prefix when generating a URL using :func:`pyramid.url.static_url`. .. note:: @@ -130,18 +128,18 @@ imperative configuration for the same purpose. other than ``media_location`` could be used. .. index:: - single: generating static resource urls - single: static resource urls + single: generating static asset urls + single: static asset urls -.. _generating_static_resource_urls: +.. _generating_static_asset_urls: -Generating Static Resource URLs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Generating Static Asset URLs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :meth:`pyramid.config.Configurator.add_static_view` method is -used to register a static resource directory, a special helper API named -:func:`pyramid.url.static_url` can be used to generate the appropriate URL for a -package resource that lives in one of the directories named by the static +When a :meth:`pyramid.config.Configurator.add_static_view` method is used to +register a static asset directory, a special helper API named +:func:`pyramid.url.static_url` can be used to generate the appropriate URL +for an asset that lives in one of the directories named by the static registration ``path`` attribute. For example, let's assume you create a set of static declarations like so: @@ -149,19 +147,19 @@ For example, let's assume you create a set of static declarations like so: .. code-block:: python :linenos: - config.add_static_view(name='static1', path='mypackage:resources/1') - config.add_static_view(name='static2', path='mypackage:resources/2') + config.add_static_view(name='static1', path='mypackage:assets/1') + config.add_static_view(name='static2', path='mypackage:assets/2') These declarations create URL-accessible directories which have URLs that -begin with ``/static1`` and ``/static2``, respectively. The resources in -the ``resources/1`` directory of the ``mypackage`` package are consulted when -a user visits a URL which begins with ``/static1``, and the resources in the -``resources/2`` directory of the ``mypackage`` package are consulted when a -user visits a URL which begins with ``/static2``. +begin with ``/static1`` and ``/static2``, respectively. The assets in the +``assets/1`` directory of the ``mypackage`` package are consulted when a user +visits a URL which begins with ``/static1``, and the assets in the +``assets/2`` directory of the ``mypackage`` package are consulted when a user +visits a URL which begins with ``/static2``. -You needn't generate the URLs to static resources "by hand" in such a -configuration. Instead, use the :func:`pyramid.url.static_url` API -to generate them for you. For example: +You needn't generate the URLs to static assets "by hand" in such a +configuration. Instead, use the :func:`pyramid.url.static_url` API to +generate them for you. For example: .. code-block:: python :linenos: @@ -170,8 +168,8 @@ to generate them for you. For example: from pyramid.chameleon_zpt import render_template_to_response def my_view(request): - css_url = static_url('mypackage:resources/1/foo.css', request) - js_url = static_url('mypackage:resources/2/foo.js', request) + css_url = static_url('mypackage:assets/1/foo.css', request) + js_url = static_url('mypackage:assets/2/foo.js', request) return render_template_to_response('templates/my_template.pt', css_url = css_url, js_url = js_url) @@ -186,13 +184,12 @@ constructing static URLs "by hand" is that if you need to change the ``name`` of a static URL declaration, the generated URLs will continue to resolve properly after the rename. -URLs may also be generated by :func:`pyramid.url.static_url` to static -resources that live *outside* the :app:`Pyramid` application. This will -happen when the :meth:`pyramid.config.Configurator.add_static_view` -API associated with the path fed to :func:`pyramid.url.static_url` is a *URL* -instead of a view name. For example, the ``name`` argument may be -``http://example.com`` while the the ``path`` given may be -``mypackage:images``: +URLs may also be generated by :func:`pyramid.url.static_url` to static assets +that live *outside* the :app:`Pyramid` application. This will happen when +the :meth:`pyramid.config.Configurator.add_static_view` API associated with +the path fed to :func:`pyramid.url.static_url` is a *URL* instead of a view +name. For example, the ``name`` argument may be ``http://example.com`` while +the the ``path`` given may be ``mypackage:images``: .. code-block:: python :linenos: @@ -200,7 +197,7 @@ instead of a view name. For example, the ``name`` argument may be config.add_static_view(name='http://example.com/images', path='mypackage:images') Under such a configuration, the URL generated by ``static_url`` for -resources which begin with ``mypackage:images`` will be prefixed with +assets which begin with ``mypackage:images`` will be prefixed with ``http://example.com/images``: .. code-block:: python @@ -210,25 +207,23 @@ resources which begin with ``mypackage:images`` will be prefixed with # -> http://example.com/images/logo.png .. index:: - single: static resource view - -Advanced: Serving Static Resources Using a View Callable --------------------------------------------------------- - -For more flexibility, static resources can be served by a :term:`view -callable` which you register manually. For example, you may want -static resources to only be available when the :term:`context` of the -view is of a particular type, or when the request is of a particular -type. - -The :class:`pyramid.view.static` helper class is used to perform -this task. This class creates an object that is capable acting as a -:app:`Pyramid` view callable which serves static resources from a -directory. For instance, to serve files within a directory located on -your filesystem at ``/path/to/static/dir`` from the URL path -``/static`` in your application, create an instance of the -:class:`pyramid.view.static` class inside a ``static.py`` file in -your application root as below. + single: static assets view + +Advanced: Serving Static Assets Using a View Callable +----------------------------------------------------- + +For more flexibility, static assets can be served by a :term:`view callable` +which you register manually. For example, you may want static assets to only +be available when the :term:`context` is of a particular type, or when +certain request headers are present. + +The :class:`pyramid.view.static` helper class is used to perform this +task. This class creates an object that is capable acting as a :app:`Pyramid` +view callable which serves static assets from a directory. For instance, to +serve files within a directory located on your filesystem at +``/path/to/static/dir`` from the URL path ``/static`` in your application, +create an instance of the :class:`pyramid.view.static` class inside a +``static.py`` file in your application root as below. .. ignore-next-block .. code-block:: python @@ -240,47 +235,43 @@ your application root as below. .. note:: the argument to :class:`pyramid.view.static` can also be a "here-relative" pathname, e.g. ``my/static`` (meaning relative to the Python package of the module in which the view is being defined). - It can also be a :term:`resource specification` + It can also be a :term:`asset specification` (e.g. ``anotherpackage:some/subdirectory``). Subsequently, you may wire this view up to be accessible as ``/static`` using -the :mod:`pyramid.config.Configurator.add_view` method in your -application's startup code against either the class or interface that -represents your root object. +the :mod:`pyramid.config.Configurator.add_view` method in your application's +startup code against either the class or interface that represents your root +resource object. .. code-block:: python :linenos: config.add_view('mypackage.static.static_view', name='static', - context='mypackage.models.Root') + context='mypackage.resources.Root') -In this case, ``mypackage.models.Root`` refers to the class of your -:app:`Pyramid` application's traversal root object. +In this case, ``mypackage.resources.Root`` refers to the class of your +:app:`Pyramid` application's resource tree. -The context argument above limits where the static view is accessible to -URL paths directly under the root object. If you omit the ``context`` -argument, then ``static`` will be accessible as the static view against -any model object in the traversal graph. This will allow -``/static/foo.js`` to work, but it will also allow for -``/anything/static/foo.js`` too, as long as ``anything`` can be -resolved. +The context argument above limits where the static view is accessible to URL +paths directly under the root object. If you omit the ``context`` argument, +then ``static`` will be accessible as the static view against any resource +object in the resource tree. This will allow ``/static/foo.js`` to work, but +it will also allow for ``/anything/static/foo.js`` too, as long as +``anything`` can be resolved. Note that you cannot use the :func:`pyramid.url.static_url` API to generate -URLs against resources made accessible by registering a custom static view. +URLs against assets made accessible by registering a custom static view. .. warning:: - When adding a static view to your root object, you need to be - careful that there are no model objects contained in the - root with the same key as the view name (e.g., ``static``). - Model objects take precedence during traversal, - thus such a name collision will cause the model to "shadow" - your static view. To avoid this issue, and ensure that your - root object's ``__getitem__`` is never - called when a static resource is requested, you can refer to them - unambiguously using the ``@@`` prefix (goggles) in their URLs. - For the above examples you could use '/@@static/foo.js' - instead of '/static/foo.js' to avoid such shadowing. - See :ref:`traversal_chapter` for information - about "goggles" (``@@``). + When adding a static view to your root object, you need to be careful that + there are no resource objects contained in the root with the same key as + the view name (e.g., ``static``). Resource objects take precedence during + traversal, thus such a name collision will cause the resource to "shadow" + your static view. To avoid this issue, and ensure that your root + resource's ``__getitem__`` is never called when a static asset is + requested, you can refer to them unambiguously using the ``@@`` prefix + (goggles) in their URLs. For the above examples you could use + '/@@static/foo.js' instead of '/static/foo.js' to avoid such shadowing. + See :ref:`traversal_chapter` for information about "goggles" (``@@``). diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index 09b9bfc51..d6c21831e 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -468,20 +468,19 @@ works in these templates. Using ZPT Macros in :app:`Pyramid` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :term:`renderer` is used to render a template, :app:`Pyramid` -makes at least two top-level names available to the template by default: -``context`` and ``request``. One of the common needs in ZPT-based -templates is to use one template's "macros" from within a different -template. In Zope, this is typically handled by retrieving the template -from the ``context``. But the context in :app:`Pyramid` is typically a -model object, and templates cannot usually be retrieved from models. To -use macros in :app:`Pyramid`, you need to make the macro template itself -available to the rendered template by passing the macro template, or -even the macro itself, *into* the rendered template. To do this you can -use the :func:`pyramid.renderers.get_renderer` API to retrieve the macro -template, and pass it into the template being rendered via the dictionary -returned by the view. For example, using a :term:`view configuration` via a -:class:`pyramid.view.view_config` decorator that uses a +When a :term:`renderer` is used to render a template, :app:`Pyramid` makes at +least two top-level names available to the template by default: ``context`` +and ``request``. One of the common needs in ZPT-based templates is to use +one template's "macros" from within a different template. In Zope, this is +typically handled by retrieving the template from the ``context``. But the +context in :app:`Pyramid` is a resource object, and templates cannot usually +be retrieved from resources. To use macros in :app:`Pyramid`, you need to make +the macro template itself available to the rendered template by passing the +macro template, or even the macro itself, *into* the rendered template. To +do this you can use the :func:`pyramid.renderers.get_renderer` API to +retrieve the macro template, and pass it into the template being rendered via +the dictionary returned by the view. For example, using a :term:`view +configuration` via a :class:`pyramid.view.view_config` decorator that uses a :term:`renderer`: .. code-block:: python @@ -658,7 +657,7 @@ on, an exception resulting from the same problem might end like so: request: <Request - at 0x1d2ecd0> project: proj macros: <Macros - at 0x1d3aed0> - context: <MyModel None at 0x1d39130> + context: <MyResource None at 0x1d39130> view: <function my_view at 0x1d23570> NameError: wrong diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index f540fa255..171eaa1c7 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -110,11 +110,9 @@ follows: View callables already have access to the request (it's passed in to each as ``request``). -- ``get_current_request`` should never be called in :term:`model` - code. Model code should never require any access to the request; if - your model code requires access to a request object, you've almost - certainly factored something wrong, and you should change your code - rather than using this function. +- ``get_current_request`` should never be called in :term:`resource` code. + If a resource needs access to the request, it should be passed the request + by a :term:`view callable`. - ``get_current_request`` function should never be called because it's "easier" or "more elegant" to think about calling it than to pass a diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 01729c4bd..c8878224e 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -3,14 +3,14 @@ Traversal ========= -:term:`Traversal` is a :term:`context finding` mechanism. It is the -act of finding a :term:`context` and a :term:`view name` by walking -over an *object graph*, starting from a :term:`root` object, using a +:term:`Traversal` is a :term:`context finding` mechanism. It is the act of +finding a :term:`context` and a :term:`view name` by walking over a +:term:`resource tree`, starting from a :term:`root` resource, using a :term:`request` object as a source of path information. -In this chapter, we'll provide a high-level overview of traversal, -we'll explain the concept of an *object graph*, and we'll show how -traversal might be used within an application. +In this chapter, we'll provide a high-level overview of traversal, we'll +explain the concept of a resource tree, and we'll show how traversal might be +used within an application. .. index:: single: traversal analogy @@ -55,16 +55,16 @@ can run the ``cat`` command: The contents of ``myfile`` are now printed on the user's behalf. -:app:`Pyramid` is very much like this inexperienced UNIX user as it -uses :term:`traversal` against an object graph. In this analogy, we -can map the ``cat`` program to the :app:`Pyramid` concept of a -:term:`view callable`: it is a program that can be run against some -:term:`context` as the result of :term:`view lookup`. The file being -operated on in this analogy is the :term:`context` object; the context -is the "last node found" in a traversal. The directory structure is -the object graph being traversed. The act of progressively changing -directories to find the file as well as the handling of a ``cd`` error -as a stop condition is analogous to :term:`traversal`. +:app:`Pyramid` is very much like this inexperienced UNIX user as it uses +:term:`traversal` against a resource tree. In this analogy, we can map the +``cat`` program to the :app:`Pyramid` concept of a :term:`view callable`: it +is a program that can be run against some :term:`context` as the result of +:term:`view lookup`. The file being operated on in this analogy is the +:term:`context` object; the context is the "last resource found" in a +traversal. The directory structure is the resource tree being traversed. +The act of progressively changing directories to find the file as well as the +handling of a ``cd`` error as a stop condition is analogous to +:term:`traversal`. The analogy we've used is not *exactly* correct, because, while the naive user already knows which command he wants to invoke before he @@ -94,9 +94,9 @@ Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of path segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is converted to the sequence ``['a', 'b', 'c']``. -After the path info is converted, a lookup is performed against the -object graph for each path segment. Each lookup uses the -``__getitem__`` method of an object in the graph. +After the path info is converted, a lookup is performed against the resource +tree for each path segment. Each lookup uses the ``__getitem__`` method of +an object in the tree. For example, if the path info sequence is ``['a', 'b', 'c']``: @@ -121,13 +121,13 @@ This process continues until the path segment sequence is exhausted or a lookup for a path element fails. In either case, a :term:`context` is found. -Traversal "stops" when it either reaches a leaf level model instance -in your object graph or when the path segments implied by the URL "run -out". The object that traversal "stops on" becomes the -:term:`context`. If at any point during traversal any node in the -graph doesn't have a ``__getitem__`` method, or if the ``__getitem__`` -method of a node raises a :exc:`KeyError`, traversal ends immediately, -and that node becomes the :term:`context`. +Traversal "stops" when it either reaches a leaf level resource in your +resource tree or when the path segments implied by the URL "run out". The +object that traversal "stops on" becomes the :term:`context`. If at any +point during traversal any resource in the tree doesn't have a +``__getitem__`` method, or if the ``__getitem__`` method of a resource raises +a :exc:`KeyError`, traversal ends immediately, and that resource becomes the +:term:`context`. The results of a :term:`traversal` also include a :term:`view name`. The :term:`view name` is the *first* URL path segment in the set of @@ -142,18 +142,18 @@ request. How :app:`Pyramid` performs view lookup is explained within the :ref:`views_chapter` chapter. .. index:: - single: object graph - single: traversal graph - single: model graph + single: object tree + single: traversal tree + single: resource tree -.. _the_object_graph: +.. _the_resource_tree: -The Object Graph ----------------- +The Resource Tree +----------------- -When your application uses :term:`traversal` to resolve URLs to code, -your application must supply an *object graph* to :app:`Pyramid`. -This graph is represented by a :term:`root` object. +When your application uses :term:`traversal` to resolve URLs to code, your +application must supply the a resource tree to :app:`Pyramid`. This tree is +represented by a :term:`root` object. In order to supply a root object for an application, at system startup time, the :app:`Pyramid` :term:`Router` is configured with a @@ -188,19 +188,18 @@ alternately be passed to the ``Configurator`` as a :term:`dotted Python name` which refers to a root factory object defined in a different module. -A root factory is passed a :term:`request` object and it is expected -to return an object which represents the root of the object graph. -All :term:`traversal` will begin at this root object. Usually a root -factory for a traversal-based application will be more complicated -than the above ``Root`` object; in particular it may be associated -with a database connection or another persistence mechanism. A root -object is often an instance of a class which has a ``__getitem__`` -method. +A root factory is passed a :term:`request` object and it is expected to +return an object which represents the root of the resource tree. All +:term:`traversal` will begin at this root object. Usually a root factory for +a traversal-based application will be more complicated than the above +``Root`` object; in particular it may be associated with a database +connection or another persistence mechanism. A root object is often an +instance of a class which has a ``__getitem__`` method. If no :term:`root factory` is passed to the :app:`Pyramid` :term:`Configurator` constructor, or the ``root_factory`` is specified as the value ``None``, a *default* root factory is used. The default -root factory always returns an object that has no child nodes. +root factory always returns an object that has no child resources. .. sidebar:: Emulating the Default Root Factory @@ -218,47 +217,45 @@ root factory always returns an object that has no child nodes. config = Configurator(root_factory=Root) The default root factory is just a really stupid object that has no - behavior or state. Using :term:`traversal` against an application - that uses the object graph supplied by the default root object is - not very interesting, because the default root object has no - children. Its availability is more useful when you're developing - an application using :term:`URL dispatch`. - -Items contained within the object graph are sometimes analogous to the -concept of :term:`model` objects used by many other frameworks (and -:app:`Pyramid` APIs often refers to them as "models", as well). -They are typically instances of Python classes. - -The object graph consists of *container* nodes and *leaf* nodes. -There is only one difference between a *container* node and a *leaf* -node: *container* nodes possess a ``__getitem__`` method while *leaf* -nodes do not. The ``__getitem__`` method was chosen as the signifying -difference between the two types of nodes because the presence of this + behavior or state. Using :term:`traversal` against an application that + uses the resource tree supplied by the default root object is not very + interesting, because the default root object has no children. Its + availability is more useful when you're developing an application using + :term:`URL dispatch`. + +Items contained within the resource tree are sometimes, in certain types of +applications, analogous to the concept of :term:`model` objects used by many +other frameworks. They are typically instances of Python classes. + +The resource tree consists of *container* resources and *leaf* resources. +There is only one difference between a *container* resource and a *leaf* +resource: *container* resources possess a ``__getitem__`` method while *leaf* +resources do not. The ``__getitem__`` method was chosen as the signifying +difference between the two types of resources because the presence of this method is how Python itself typically determines whether an object is "containerish" or not. -Each container node is presumed to be willing to return a child node +Each container resource is presumed to be willing to return a child resource or raise a ``KeyError`` based on a name passed to its ``__getitem__``. Leaf-level instances must not have a ``__getitem__``. If instances that you'd like to be leaves already happen to have a ``__getitem__`` through some historical inequity, you should subclass -these node types and cause their ``__getitem__`` methods to simply +these resource types and cause their ``__getitem__`` methods to simply raise a ``KeyError``. Or just disuse them and think up another strategy. -Usually, the traversal root is a *container* node, and as such it -contains other nodes. However, it doesn't *need* to be a container. -Your object graph can be as shallow or as deep as you require. +Usually, the traversal root is a *container* resource, and as such it +contains other resources. However, it doesn't *need* to be a container. +Your resource tree can be as shallow or as deep as you require. -In general, the object graph is traversed beginning at its root object -using a sequence of path elements described by the ``PATH_INFO`` of -the current request; if there are path segments, the root object's -``__getitem__`` is called with the next path segment, and it is -expected to return another graph object. The resulting object's -``__getitem__`` is called with the very next path segment, and it is -expected to return another graph object. This happens *ad infinitum* -until all path segments are exhausted. +In general, the resource tree is traversed beginning at its root object using +a sequence of path elements described by the ``PATH_INFO`` of the current +request; if there are path segments, the root object's ``__getitem__`` is +called with the next path segment, and it is expected to return another +resource object. The resulting object's ``__getitem__`` is called with the +very next path segment, and it is expected to return another resource object. +This happens *ad infinitum* until all path segments are exhausted. .. index:: single: traversal algorithm @@ -273,7 +270,7 @@ This section will attempt to explain the :app:`Pyramid` traversal algorithm. We'll provide a description of the algorithm, a diagram of how the algorithm works, and some example traversal scenarios that might help you understand how the algorithm operates against a -specific object graph. +specific resource tree. We'll also talk a bit about :term:`view lookup`. The :ref:`views_chapter` chapter discusses :term:`view lookup` in detail, @@ -314,17 +311,15 @@ and a :term:`view name`. stripped off ``PATH_INFO``, and the remaining path segments are split on the slash character to form a traversal sequence. - The traversal algorithm by default attempts to first URL-unquote - and then Unicode-decode each path segment derived from - ``PATH_INFO`` from its natural byte string (``str`` type) - representation. URL unquoting is performed using the Python - standard library ``urllib.unquote`` function. Conversion from a - URL-decoded string into Unicode is attempted using the UTF-8 - encoding. If any URL-unquoted path segment in ``PATH_INFO`` is - not decodeable using the UTF-8 decoding, a :exc:`TypeError` is - raised. A segment will be fully URL-unquoted and UTF8-decoded - before it is passed it to the ``__getitem__`` of any model object - during traversal. + The traversal algorithm by default attempts to first URL-unquote and then + Unicode-decode each path segment derived from ``PATH_INFO`` from its + natural byte string (``str`` type) representation. URL unquoting is + performed using the Python standard library ``urllib.unquote`` function. + Conversion from a URL-decoded string into Unicode is attempted using the + UTF-8 encoding. If any URL-unquoted path segment in ``PATH_INFO`` is not + decodeable using the UTF-8 decoding, a :exc:`TypeError` is raised. A + segment will be fully URL-unquoted and UTF8-decoded before it is passed + it to the ``__getitem__`` of any resource during traversal. Thus, a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the traversal sequence ``[u'a', u'b', u'c']``. @@ -340,12 +335,11 @@ and a :term:`view name`. for the name ``c``, and may return "object ``c``". #. Traversal ends when a) the entire path is exhausted or b) when any - graph element raises a :exc:`KeyError` from its ``__getitem__`` or - c) when any non-final path element traversal does not have a - ``__getitem__`` method (resulting in a :exc:`NameError`) or d) - when any path element is prefixed with the set of characters - ``@@`` (indicating that the characters following the ``@@`` token - should be treated as a :term:`view name`). + resouce raises a :exc:`KeyError` from its ``__getitem__`` or c) when any + non-final path element traversal does not have a ``__getitem__`` method + (resulting in a :exc:`NameError`) or d) when any path element is prefixed + with the set of characters ``@@`` (indicating that the characters + following the ``@@`` token should be treated as a :term:`view name`). #. When traversal ends for any of the reasons in the previous step, the last object found during traversal is deemed to be the @@ -375,19 +369,18 @@ The traversal algorithm exposes two special cases: The default view is a view that is registered with no name or a view which is registered with a name that equals the empty string. -- If any path segment element begins with the special characters - ``@@`` (think of them as goggles), the value of that segment minus - the goggle characters is considered the :term:`view name` - immediately and traversal stops there. This allows you to address - views that may have the same names as model instance names in the - graph unambiguously. +- If any path segment element begins with the special characters ``@@`` + (think of them as goggles), the value of that segment minus the goggle + characters is considered the :term:`view name` immediately and traversal + stops there. This allows you to address views that may have the same names + as resource names in the tree unambiguously. Finally, traversal is responsible for locating a :term:`virtual root`. A virtual root is used during "virtual hosting"; see the :ref:`vhosting_chapter` chapter for information. We won't speak more about it in this chapter. -.. image:: modelgraphtraverser.png +.. image:: resourcetreetraverser.png .. index:: single: traversal examples @@ -397,13 +390,13 @@ Traversal Algorithm Examples No one can be expected to understand the traversal algorithm by analogy and description alone, so let's examine some traversal -scenarios that use concrete URLs and object graph compositions. +scenarios that use concrete URLs and resource tree compositions. Let's pretend the user asks for ``http://example.com/foo/bar/baz/biz/buz.txt``. The request's ``PATH_INFO`` in that case is ``/foo/bar/baz/biz/buz.txt``. Let's further pretend that when this request comes in that we're traversing -the following object graph: +the following resource tree: .. code-block:: text @@ -450,7 +443,7 @@ Let's say that view lookup finds no matching view type. In this circumstance, the :app:`Pyramid` :term:`router` returns the result of the :term:`not found view` and the request ends. -However, for this graph: +However, for this tree: .. code-block:: text @@ -537,6 +530,6 @@ The :mod:`pyramid.traversal` module contains API functions that deal with traversal, such as traversal invocation from within application code. -The :func:`pyramid.url.model_url` function generates a URL when -given an object retrieved from an object graph. +The :func:`pyramid.url.resource_url` function generates a URL when +given an object retrieved from an resource tree. diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index 3c31f69b6..6a721f403 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -12,23 +12,22 @@ your application. In this context, a "unit" is often a function or a method of a class instance. The unit is also referred to as a "unit under test". -The goal of a single unit test is to test **only** some permutation of -the "unit under test". If you write a unit test that aims to verify -the result of a particular codepath through a Python function, you -need only be concerned about testing the code that *lives in the -function body itself*. If the function accepts a parameter that -represents a complex application "domain object" (such as a model, a -database connection, or an SMTP server), the argument provided to this -function during a unit test *need not be* and likely *should not be* a -"real" implementation object. For example, although a particular -function implementation may accept an argument that represents an SMTP -server object, and the function may call a method of this object when -the system is operating normally that would result in an email being -sent, a unit test of this codepath of the function does *not* need to -test that an email is actually sent. It just needs to make sure that -the function calls the method of the object provided as an argument -that *would* send an email if the argument happened to be the "real" -implementation of an SMTP server object. +The goal of a single unit test is to test **only** some permutation of the +"unit under test". If you write a unit test that aims to verify the result +of a particular codepath through a Python function, you need only be +concerned about testing the code that *lives in the function body itself*. +If the function accepts a parameter that represents a complex application +"domain object" (such as a resource, a database connection, or an SMTP +server), the argument provided to this function during a unit test *need not +be* and likely *should not be* a "real" implementation object. For example, +although a particular function implementation may accept an argument that +represents an SMTP server object, and the function may call a method of this +object when the system is operating normally that would result in an email +being sent, a unit test of this codepath of the function does *not* need to +test that an email is actually sent. It just needs to make sure that the +function calls the method of the object provided as an argument that *would* +send an email if the argument happened to be the "real" implementation of an +SMTP server object. An *integration test*, on the other hand, is a different form of testing in which the interaction between two or more "units" is @@ -298,9 +297,9 @@ sure to use this pattern in your test case's ``setUp`` and See the :ref:`testing_module` chapter for the entire :app:`Pyramid` -specific testing API. This chapter describes APIs for registering a -security policy, registering models at paths, registering event +security policy, registering resources at paths, registering event listeners, registering views and view permissions, and classes -representing "dummy" implementations of a request and a model. +representing "dummy" implementations of a request and a resource. See also the various methods of the :term:`Configurator` documented in :ref:`configuration_module` that begin with the ``testing_`` prefix. diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index f0ace76ba..8adeb2511 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -375,7 +375,7 @@ order of route configuration declarations is very important. The order that routes declarations are evaluated is the order in which they are added to the application at startup time. This is unlike :term:`traversal`, which depends on emergent behavior which happens as -a result of traversing a graph. +a result of traversing a resource tree. For routes added via the :mod:`pyramid.config.Configurator.add_route` method, the order that routes are evaluated is the order in which they are @@ -414,7 +414,7 @@ found via :term:`view lookup`. :linenos: config.add_route('abc', '/abc', view='myproject.views.theview', - factory='myproject.models.root_factory') + factory='myproject.resources.root_factory') The factory can either be a Python object or a :term:`dotted Python name` (a string) which points to such a Python object, as it is above. @@ -459,7 +459,7 @@ represent neither predicates nor view configuration information. A Python object (often a function or a class) or a :term:`dotted Python name` to such an object that will generate a :app:`Pyramid` :term:`context` object when this route - matches. For example, ``mypackage.models.MyFactoryClass``. If this + matches. For example, ``mypackage.resources.MyFactoryClass``. If this argument is not specified, the traversal root factory will be used. ``traverse`` @@ -979,10 +979,10 @@ An example of using a route with a factory: config.add_route('idea', 'ideas/{idea}', view='myproject.views.idea_view', - factory='myproject.models.Idea') + factory='myproject.resources.Idea') -The above route will manufacture an ``Idea`` model as a -:term:`context`, assuming that ``mypackage.models.Idea`` resolves to a +The above route will manufacture an ``Idea`` resource as a +:term:`context`, assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a request in its ``__init__``. For example: .. code-block:: python diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index d1fd1b382..65168806e 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -85,21 +85,19 @@ Virtual Root Support :term:`traversal` -based (but not :term:`URL dispatch` -based) applications. -Virtual root support is useful when you'd like to host some model in a -:app:`Pyramid` object graph as an application under a URL pathname -that does not include the model path itself. For example, you might -want to serve the object at the traversal path ``/cms`` as an -application reachable via ``http://example.com/`` (as opposed to -``http://example.com/cms``). - -To specify a virtual root, cause an environment variable to be -inserted into the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value -that is the absolute pathname to the model object in the traversal -graph that should behave as the "root" model. As a result, the -traversal machinery will respect this value during traversal -(prepending it to the PATH_INFO before traversal starts), and the -:func:`pyramid.url.model_url` API will generate the "correct" -virtually-rooted URLs. +Virtual root support is useful when you'd like to host some resource in a +:app:`Pyramid` resource tree as an application under a URL pathname that does +not include the resource path itself. For example, you might want to serve the +object at the traversal path ``/cms`` as an application reachable via +``http://example.com/`` (as opposed to ``http://example.com/cms``). + +To specify a virtual root, cause an environment variable to be inserted into +the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value that is the absolute +pathname to the resource object in the resource tree that should behave as +the "root" resource. As a result, the traversal machinery will respect this +value during traversal (prepending it to the PATH_INFO before traversal +starts), and the :func:`pyramid.url.resource_url` API will generate the +"correct" virtually-rooted URLs. An example of an Apache ``mod_proxy`` configuration that will host the ``/cms`` subobject as ``http://www.example.com/`` using this facility diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 8a689be21..dccbc6b5a 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -3,11 +3,10 @@ Views ===== -The primary job of any :app:`Pyramid` application is is to find and -invoke a :term:`view callable` when a :term:`request` reaches the -application. View callables are bits of code written by you -- the -application developer -- which do something interesting in response to -a request made to your application. +The primary job of any :app:`Pyramid` application is is to find and invoke a +:term:`view callable` when a :term:`request` reaches the application. View +callables are bits of code which do something interesting in response to a +request made to your application. .. note:: @@ -18,11 +17,10 @@ a request made to your application. that implements a view *callable*, and the process of view *lookup*. -The chapter :ref:`contextfinding_chapter` describes how, using -information from the :term:`request`, a :term:`context` and a -:term:`view name` are computed. But neither the context nor the view -name found are very useful unless those elements can eventually be -mapped to a :term:`view callable`. +The chapter :ref:`contextfinding_chapter` describes how, using information +from the :term:`request`, a :term:`context` and a :term:`view name` are +computed. But neither the context nor the view name found are very useful +unless those elements can eventually be mapped to a :term:`view callable`. The job of actually locating and invoking the "best" :term:`view callable` is the job of the :term:`view lookup` subsystem. The view @@ -139,9 +137,8 @@ represent the method expected to return a response, you can use an function defined in this style can be defined as follows: context - An instance of a :term:`context` found via graph :term:`traversal` - or :term:`URL dispatch`. If the context is found via traversal, it - will be a :term:`model` object. + The :term:`resource` object found via tree :term:`traversal` + or :term:`URL dispatch`. request A :app:`Pyramid` Request object representing the current WSGI @@ -476,7 +473,7 @@ You can configure a view to use the JSON renderer by naming ``json`` as the config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='json') @@ -494,22 +491,21 @@ attributes by attaching properties to the request. See Two built-in renderers exist for :term:`Chameleon` templates. -If the ``renderer`` attribute of a view configuration is an absolute -path, a relative path or :term:`resource specification` which has a -final path element with a filename extension of ``.pt``, the Chameleon -ZPT renderer is used. See :ref:`chameleon_zpt_templates` for more -information about ZPT templates. +If the ``renderer`` attribute of a view configuration is an absolute path, a +relative path or :term:`asset specification` which has a final path element +with a filename extension of ``.pt``, the Chameleon ZPT renderer is used. +See :ref:`chameleon_zpt_templates` for more information about ZPT templates. If the ``renderer`` attribute of a view configuration is an absolute path or -a :term:`resource specification` which has a final path element with a -filename extension of ``.txt``, the :term:`Chameleon` text renderer is used. -See :ref:`chameleon_zpt_templates` for more information about Chameleon text +a :term:`asset specification` which has a final path element with a filename +extension of ``.txt``, the :term:`Chameleon` text renderer is used. See +:ref:`chameleon_zpt_templates` for more information about Chameleon text templates. The behavior of these renderers is the same, except for the engine used to render the template. -When a ``renderer`` attribute that names a template path or :term:`resource +When a ``renderer`` attribute that names a template path or :term:`asset specification` (e.g. ``myproject:templates/foo.pt`` or ``myproject:templates/foo.txt``) is used, the view must return a :term:`Response` object or a Python *dictionary*. If the view callable with @@ -541,7 +537,7 @@ renderer: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='myproject:templates/foo.pt') Here's an example view configuration which uses a Chameleon text @@ -552,7 +548,7 @@ renderer: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='myproject:templates/foo.txt') Views which use a Chameleon renderer can vary response attributes by @@ -573,13 +569,13 @@ The dictionary items will then be used in the global template space. If the view callable returns anything but a Response object, or a dictionary, an error will be raised. -When using a ``renderer`` argument to a :term:`view configuration` to -specify a Mako template, the value of the ``renderer`` may be a path -relative to the ``mako.directories`` setting (e.g. -``some/template.mak``) or, alternately, it may be a :term:`resource -specification` (e.g. ``apackage:templates/sometemplate.mak``). Mako -templates may internally inherit other Mako templates using a relative -filename or a :term:`resource specification` as desired. +When using a ``renderer`` argument to a :term:`view configuration` to specify +a Mako template, the value of the ``renderer`` may be a path relative to the +``mako.directories`` setting (e.g. ``some/template.mak``) or, alternately, +it may be a :term:`asset specification` +(e.g. ``apackage:templates/sometemplate.mak``). Mako templates may +internally inherit other Mako templates using a relative filename or a +:term:`asset specification` as desired. XXX Further explanation or link to mako inheritance info @@ -592,7 +588,7 @@ Here's an example view configuration which uses a relative path: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='foo.mak') It's important to note that in Mako's case, the 'relative' path name @@ -600,16 +596,15 @@ It's important to note that in Mako's case, the 'relative' path name directory (or directories) configured for Mako via the ``mako.directories`` configuration file setting. -The renderer can also be provided in :term:`resource specification` -format. Here's an example view configuration which uses a :term:`resource -specification`: +The renderer can also be provided in :term:`asset specification` +format. Here's an example view configuration which uses one: .. code-block:: python :linenos: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='mypackage:templates/foo.mak') The above configuration will use the file named ``foo.mak`` in the @@ -746,26 +741,27 @@ factory constructor is available as :class:`pyramid.interfaces.IRendererInfo`. There are essentially two different kinds of renderer factories: -- A renderer factory which expects to accept a :term:`resource specification`, - or an absolute path, as the ``name`` attribute of the ``info`` object fed to - its constructor. These renderer factories are registered with a ``name`` - value that begins with a dot (``.``). These types of renderer factories - usually relate to a file on the filesystem, such as a template. - -- A renderer factory which expects to accept a token that does not represent a - filesystem path or a resource specification in the ``name`` attribute of the +- A renderer factory which expects to accept a :term:`asset + specification`, or an absolute path, as the ``name`` attribute of the ``info`` object fed to its constructor. These renderer factories are - registered with a ``name`` value that does not begin with a dot. These - renderer factories are typically object serializers. + registered with a ``name`` value that begins with a dot (``.``). These + types of renderer factories usually relate to a file on the filesystem, + such as a template. + +- A renderer factory which expects to accept a token that does not represent + a filesystem path or a asset specification in the ``name`` + attribute of the ``info`` object fed to its constructor. These renderer + factories are registered with a ``name`` value that does not begin with a + dot. These renderer factories are typically object serializers. -.. sidebar:: Resource Specifications +.. sidebar:: Asset Specifications - A resource specification is a colon-delimited identifier for a - :term:`resource`. The colon separates a Python :term:`package` - name from a package subpath. For example, the resource - specification ``my.package:static/baz.css`` identifies the file - named ``baz.css`` in the ``static`` subdirectory of the - ``my.package`` Python :term:`package`. + A asset specification is a colon-delimited identifier for a + :term:`asset`. The colon separates a Python :term:`package` + name from a package subpath. For example, the asset + specification ``my.package:static/baz.css`` identifies the file named + ``baz.css`` in the ``static`` subdirectory of the ``my.package`` Python + :term:`package`. Here's an example of the registration of a simple renderer factory via :meth:`pyramid.config.Configurator.add_renderer`: @@ -1211,30 +1207,26 @@ Non-Predicate Arguments itself if the view is a function, or the ``__call__`` callable attribute if the view is a class). -``renderer`` - This is either a single string term (e.g. ``json``) or a string - implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``) naming a :term:`renderer` - implementation. If the ``renderer`` value does not contain a dot - (``.``), the specified string will be used to look up a renderer - implementation, and that renderer implementation will be used to - construct a response from the view return value. If the - ``renderer`` value contains a dot (``.``), the specified term will - be treated as a path, and the filename extension of the last element - in the path will be used to look up the renderer implementation, - which will be passed the full path. The renderer implementation - will be used to construct a :term:`response` from the view return - value. - - When the renderer is a path, although a path is usually just a - simple relative pathname (e.g. ``templates/foo.pt``, implying that a - template named "foo.pt" is in the "templates" directory relative to - the directory of the current :term:`package`), a path can be - absolute, starting with a slash on UNIX or a drive letter prefix on - Windows. The path can alternately be a :term:`resource - specification` in the form - ``some.dotted.package_name:relative/path``, making it possible to - address template resources which live in a separate package. +``renderer`` This is either a single string term (e.g. ``json``) or a string + implying a path or :term:`asset specification` + (e.g. ``templates/views.pt``) naming a :term:`renderer` implementation. If + the ``renderer`` value does not contain a dot (``.``), the specified string + will be used to look up a renderer implementation, and that renderer + implementation will be used to construct a response from the view return + value. If the ``renderer`` value contains a dot (``.``), the specified + term will be treated as a path, and the filename extension of the last + element in the path will be used to look up the renderer implementation, + which will be passed the full path. The renderer implementation will be + used to construct a :term:`response` from the view return value. + + When the renderer is a path, although a path is usually just a simple + relative pathname (e.g. ``templates/foo.pt``, implying that a template + named "foo.pt" is in the "templates" directory relative to the directory of + the current :term:`package`), a path can be absolute, starting with a slash + on UNIX or a drive letter prefix on Windows. The path can alternately be a + :term:`asset specification` in the form + ``some.dotted.package_name:relative/path``, making it possible to address + template assets which live in a separate package. The ``renderer`` attribute is optional. If it is not defined, the "null" renderer is assumed (no rendering is performed and the value @@ -1276,15 +1268,15 @@ the usage of the configured view. default view). ``context`` - An object representing a Python class that the :term:`context` must be - an instance of, *or* the :term:`interface` that the :term:`context` - must provide in order for this view to be found and called. This - predicate is true when the :term:`context` is an instance of the - represented class or if the :term:`context` provides the represented - interface; it is otherwise false. + An object representing a Python class that the :term:`context` resource + must be an instance of, *or* the :term:`interface` that the :term:`context` + must provide in order for this view to be found and called. This predicate + is true when the :term:`context` is an instance of the represented class or + if the :term:`context` provides the represented interface; it is otherwise + false. - If ``context`` is not supplied, the value ``None``, which matches - any model, is used. + If ``context`` is not supplied, the value ``None``, which matches any + resource, is used. ``route_name`` If ``route_name`` is supplied, the view callable will be invoked @@ -1344,7 +1336,7 @@ the usage of the configured view. This value should be a reference to a Python class or :term:`interface` that a parent object in the context's :term:`lineage` must provide in order for this view to be found and - called. The nodes in your object graph must be "location-aware" to + called. The resources in your resource tree must be "location-aware" to use this feature. If ``containment`` is not supplied, the interfaces and classes in @@ -1469,11 +1461,11 @@ reside in a :app:`Pyramid` application module ``views.py``: .. code-block:: python :linenos: - from models import MyModel + from resources import MyResource from pyramid.view import view_config from pyramid.chameleon_zpt import render_template_to_response - @view_config(name='my_view', request_method='POST', context=MyModel, + @view_config(name='my_view', request_method='POST', context=MyResource, permission='read', renderer='templates/my.pt') def my_view(request): return {'a':1} @@ -1486,7 +1478,7 @@ configuration stanza: :linenos: config.add_view('.views.my_view', name='my_view', request_method='POST', - context=MyModel, permission='read') + context=MyResource, permission='read') All arguments to ``view_config`` may be omitted. For example: @@ -1503,7 +1495,7 @@ All arguments to ``view_config`` may be omitted. For example: Such a registration as the one directly above implies that the view name will be ``my_view``, registered with a ``context`` argument that -matches any model type, using no permission, registered against +matches any resource type, using no permission, registered against requests with any request method, request type, request param, route name, or containment. @@ -1686,32 +1678,32 @@ such an object. All other arguments are optional. See information. .. index:: - single: model interfaces + single: resource interfaces -.. _using_model_interfaces: +.. _using_resource_interfaces: -Using Model Interfaces In View Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using Resource Interfaces In View Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of registering your views with a ``context`` that names a -Python model *class*, you can optionally register a view callable with -a ``context`` which is an :term:`interface`. An interface can be -attached arbitrarily to any model instance. View lookup treats -context interfaces specially, and therefore the identity of a model -can be divorced from that of the class which implements it. As a -result, associating a view with an interface can provide more -flexibility for sharing a single view between two or more different -implementations of a model type. For example, if two model object -instances of different Python class types share the same interface, -you can use the same view against each of them. +Instead of registering your views with a ``context`` that names a Python +resource *class*, you can optionally register a view callable with a +``context`` which is an :term:`interface`. An interface can be attached +arbitrarily to any resource object. View lookup treats context interfaces +specially, and therefore the identity of a resource can be divorced from that +of the class which implements it. As a result, associating a view with an +interface can provide more flexibility for sharing a single view between two +or more different implementations of a resource type. For example, if two +resource objects of different Python class types share the same interface, +you can use the same view configuration to specify both of them as a +``context``. -In order to make use of interfaces in your application during view -dispatch, you must create an interface and mark up your model classes -or instances with interface declarations that refer to this interface. +In order to make use of interfaces in your application during view dispatch, +you must create an interface and mark up your resource classes or instances +with interface declarations that refer to this interface. -To attach an interface to a model *class*, you define the interface -and use the :func:`zope.interface.implements` function to associate -the interface with the class. +To attach an interface to a resource *class*, you define the interface and +use the :func:`zope.interface.implements` function to associate the interface +with the class. .. code-block:: python :linenos: @@ -1725,10 +1717,10 @@ the interface with the class. class Hello(object): implements(IHello) -To attach an interface to a model *instance*, you define the interface -and use the :func:`zope.interface.alsoProvides` function to associate -the interface with the instance. This function mutates the instance -in such a way that the interface is attached to it. +To attach an interface to a resource *instance*, you define the interface and +use the :func:`zope.interface.alsoProvides` function to associate the +interface with the instance. This function mutates the instance in such a +way that the interface is attached to it. .. code-block:: python :linenos: @@ -1747,13 +1739,13 @@ in such a way that the interface is attached to it. alsoProvides(hello, IHello) return hello -Regardless of how you associate an interface, with a model instance, or a model -class, the resulting code to associate that interface with a view callable is -the same. Assuming the above code that defines an ``IHello`` interface lives -in the root of your application, and its module is named "models.py", the -interface declaration below will associate the -``mypackage.views.hello_world`` view with models that implement, or provide, -this interface. +Regardless of how you associate an interface, with a resource instance, or a +resource class, the resulting code to associate that interface with a view +callable is the same. Assuming the above code that defines an ``IHello`` +interface lives in the root of your application, and its module is named +"resources.py", the interface declaration below will associate the +``mypackage.views.hello_world`` view with resources that implement, or +provide, this interface. .. code-block:: python :linenos: @@ -1761,22 +1753,25 @@ this interface. # config is an instance of pyramid.config.Configurator config.add_view('mypackage.views.hello_world', name='hello.html', - context='mypackage.models.IHello') + context='mypackage.resources.IHello') -Any time a model that is determined to be the :term:`context` provides this -interface, and a view named ``hello.html`` is looked up against it as per the -URL, the ``mypackage.views.hello_world`` view callable will be invoked. +Any time a resource that is determined to be the :term:`context` provides +this interface, and a view named ``hello.html`` is looked up against it as +per the URL, the ``mypackage.views.hello_world`` view callable will be +invoked. -Note, in cases where a view is registered against a model class, and a -view is also registered against an interface that the model class -implements, an ambiguity arises. Views registered for the model class -take precedence over any views registered for any interface the model -class implements. Thus, if a view is registered for both the class type -of the context and an interface implemented by the context's class, the -view registered for the context's class will "win". +Note, in cases where a view is registered against a resource class, and a +view is also registered against an interface that the resource class +implements, an ambiguity arises. Views registered for the resource class take +precedence over any views registered for any interface the resource class +implements. Thus, if one view configuration names a ``context`` of both the +class type of a resource, and another view configuration names a ``context`` +of interface implemented by the resource's class, and both view +configurations are otherwise identical, the view registered for the context's +class will "win". -For more information about defining models with interfaces for use within -view configuration, see :ref:`models_which_implement_interfaces`. +For more information about defining resources with interfaces for use within +view configuration, see :ref:`resources_which_implement_interfaces`. .. index:: single: view security @@ -1787,22 +1782,12 @@ view configuration, see :ref:`models_which_implement_interfaces`. Configuring View Security ~~~~~~~~~~~~~~~~~~~~~~~~~ -<<<<<<< HEAD -If an :term:`authorization policy` is active, any :term:`permission` -attached to a :term:`view configuration` found during view lookup will -be verified. This will ensure that the currently authenticated user -possesses that permission against the :term:`context` before the view -function is actually called. Here's an example of specifying a -permission in a view configuration using -:meth:`pyramid.configuration.Configurator.add_view`: -======= If a :term:`authorization policy` is active, any :term:`permission` attached -to a :term:`view configuration` found during view lookup will be consulted to -ensure that the currently authenticated user possesses that permission -against the :term:`context` before the view function is actually called. -Here's an example of specifying a permission in a view configuration using -:meth:`pyramid.config.Configurator.add_view`: ->>>>>>> fee38663daccc0130d0c34dbc5a14e67bef2e183 +to a :term:`view configuration` found during view lookup will be verified. +This will ensure that the currently authenticated user possesses that +permission against the :term:`context` before the view function is actually +called. Here's an example of specifying a permission in a view configuration +using :meth:`pyramid.config.Configurator.add_view`: .. code-block:: python :linenos: @@ -1810,7 +1795,7 @@ Here's an example of specifying a permission in a view configuration using # config is an instance of pyramid.config.Configurator config.add_view('myproject.views.add_entry', name='add.html', - context='myproject.models.IBlog', permission='add') + context='myproject.resources.IBlog', permission='add') When an :term:`authorization policy` is enabled, this view will be protected with the ``add`` permission. The view will *not be called* if diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst index c930ecc50..f330fd551 100644 --- a/docs/narr/zca.rst +++ b/docs/narr/zca.rst @@ -129,7 +129,7 @@ interface of a registry instead, you need only know how to obtain the There are two ways of doing so: - use the :func:`pyramid.threadlocal.get_current_registry` - function within :app:`Pyramid` view or model code. This will + function within :app:`Pyramid` view or resource code. This will always return the "current" :app:`Pyramid` application registry. - use the attribute of the :term:`request` object named ``registry`` |
