Rob Miller
2011-01-13 6fd291025098dfeba2872459a0a9ff64f9de3dce
Minor edits, typo fixes, term consistency changes.
1 files modified
79 ■■■■ changed files
docs/narr/muchadoabouttraversal.rst 79 ●●●● patch | view | raw | blame | history
docs/narr/muchadoabouttraversal.rst
@@ -43,12 +43,12 @@
URL Dispatch
------------
Let's step back and consider the problem we're trying to solve, which is
simple.  An HTTP request for a particular path has been routed to our web
application.  The requested path will possibly invoke a specific :term:`view
callable` function defined somewhere in our app.  We're trying to determine
*which* callable function, if any, should be invoked for a given requested
URL.
Let's step back and consider the problem we're trying to solve.  An
HTTP request for a particular path has been routed to our web
application.  The requested path will possibly invoke a specific
:term:`view callable` function defined somewhere in our app.  We're
trying to determine *which* callable function, if any, should be
invoked for a given requested URL.
Many systems, including Pyramid, offer a simple solution.  They offer the
concept of "URL matching".  URL matching approaches this problem by parsing
@@ -59,10 +59,9 @@
If the request path matches more than one pattern, some conflict resolution
scheme is used, usually a simple order precedence so that the first match
will take priority over any subsequent matches.  If a request path doesn't
match any of the defined patterns, :app:`Pyramid` a "404 Not Found" response
is returned.
match any of the defined patterns, a "404 Not Found" response is returned.
In Pyramid, we offer an implementation of URL mapping which we call
In Pyramid, we offer an implementation of URL matching which we call
:term:`URL dispatch`.  Using :app:`Pyramid` syntax, we might have a match
pattern such as ``/{userid}/photos/{photoid}``, mapped to a ``photo_view()``
function defined somewhere in our code.  Then a request for a path such as
@@ -115,7 +114,7 @@
The major difference between file system lookup and traversal is that a file
system lookup steps through nested directories and files in a file system
tree, while traversal steps through nested dictionary-type objects in an
tree, while traversal steps through nested dictionary-type objects in a
:term:`resource tree`.  Let's take a detailed look at one of our example
paths, so we can see what I mean:
@@ -123,7 +122,7 @@
``joeschmoe``, ``photos`` and ``photo1``.  With file system lookup we might
have a root folder (``/``) containing a nested folder (``joeschmoe``), which
contains another nested folder (``photos``), which finally contains a JPG
file ("photo1").  With traversal, we instead have a dictionary-like root
file (``photo1``).  With traversal, we instead have a dictionary-like root
object.  Asking for the ``joeschmoe`` key gives us another dictionary-like
object.  Asking this in turn for the ``photos`` key gives us yet another
mapping object, which finally (hopefully) contains the resource that we're
@@ -154,9 +153,9 @@
restriction on how a :term:`resource` is implemented; a developer can
implement them as he wishes.  One common pattern used is to persist all of
the resources, including the root, in a database as a graph.  The root object
is a dictionarylike object.  Dictionarylike objects in Python supply a
is a dictionary-like object.  Dictionary-like objects in Python supply a
``__getitem__`` method which is called when key lookup is done.  Under the
hood, when ``adict`` is a dictionarylike object, Python translates
hood, when ``adict`` is a dictionary-like object, Python translates
``adict['a']`` to ``adict.__getitem__('a')``.  Try doing this in a Python
interpreter prompt if you don't believe us:
@@ -174,7 +173,7 @@
   1
The dictionarylike root object stores the ids of all of its subresources as
The dictionary-like root object stores the ids of all of its subresources as
keys, and provides a ``__getitem__`` implementation that fetches them.  So
``get_root()`` fetches the unique root object, while
``get_root()['joeschmoe']`` returns a different object, also stored in the
@@ -185,12 +184,13 @@
provide the dictionary-like API (i.e. as long as they have an appropriately
implemented ``__getitem__`` method) then traversal will work.
In fact, you don't need a "database" at all.  You could trivially implement a
set of objects with ``__getitem__`` methods that search for files in specific
directories, and thus precisely recreate the older mechanism of having the
URL path mapped directly to a folder structure on the file system.  Or you
could use plain dictionaries too.  Traversal is in fact a superset of file
system lookup.
In fact, you don't need a "database" at all.  You could use plain
dictionaries, with your site's URL structure hard-coded directly in
the Python source.  Or you could trivially implement a set of objects
with ``__getitem__`` methods that search for files in specific
directories, and thus precisely recreate the traditional mechanism of
having the URL path mapped directly to a folder structure on the file
system.  Traversal is in fact a superset of file system lookup.
.. note:: See the chapter entitled :ref:`resources_chapter` for a more
   technical overview of resources.
@@ -207,13 +207,14 @@
example, for instance, you might want to view the photo in a page, but you
might also want to provide a way for the user to edit the photo and any
associated metadata.  We'll call the former the ``view`` view, and the latter
will be the ``edit`` view (Original, I know.)  :app:`Pyramid` has a
centralized view registry where named views can be associated with specific
resource types.  So in our example, we'll assume that we've registered
``view`` and ``edit`` views for photo objects, and that we've specified the
``view`` view as the default, so that ``/joeschmoe/photos/photo1/view`` and
``/joeschmoe/photos/photo1`` are equivalent.  The edit view would sensibly be
provided by a request for ``/joeschmoe/photos/photo1/edit``.
will be the ``edit`` view.  (Original, I know.)  :app:`Pyramid` has a
centralized view :term:`registry` where named views can be associated with
specific resource types.  So in our example, we'll assume that we've
registered ``view`` and ``edit`` views for photo objects, and that we've
specified the ``view`` view as the default, so that
``/joeschmoe/photos/photo1/view`` and ``/joeschmoe/photos/photo1`` are
equivalent.  The edit view would sensibly be provided by a request for
``/joeschmoe/photos/photo1/edit``.
Hopefully it's clear that the first portion of the edit view's URL path is
going to resolve to the same resource as the non-edit version, specifically
@@ -232,15 +233,15 @@
You might conceptualize a request for ``/joeschmoe/photos/photo1/edit`` as
ultimately converted into the following piece of Pythonic pseudocode::
  context = get_root()['joeschmoe']['photos']['photo1']
  view_callable = get_view(context, 'edit')
  request.context = context
  view_callable(request)
The ``get_root`` and ``get_view`` functions don't really exist.  Internally,
:app:`Pyramid` does something more complicated.  But the example above is a
reasonable approximation of the view lookup algorithm in pseudocode.
:app:`Pyramid` does something more complicated.  But the example above
is a reasonable approximation of the view lookup algorithm in pseudocode.
Use Cases
---------
@@ -264,20 +265,18 @@
construct matching patterns that could account for every possible combination
of paths that might develop?
It's possible, but it will make for some somewhat ugly URLs.  And the
matching patterns are going to become complex quickly as you try to handle
all of the edge cases.
It might be possible, but it certainly won't be easy.  The matching
patterns are going to become complex quickly as you try to handle all
of the edge cases.
With traversal, however, it's straightforward.  If you want 20 layers of
nesting, it's no problem.  :app:`Pyramid` will happily call ``__getitem__``
With traversal, however, it's straightforward.  20 layers of nesting would be
 no problem.  :app:`Pyramid` will happily call ``__getitem__``
as many times as it needs to, until it runs out of path segments or until a
resource raises a :exc:`KeyError`.  Each resource only needs to know how to
fetch its immediate children, the traversal algorithm takes care of the rest.
One of the key advantages of traversal is that the structure of the resource
tree can live in the database, and not in the code. It's simple to let users
modify the tree at runtime to set up their own personalized directory
structures.
Also, since the structure of the resource tree can live in the database and
not in the code, it's simple to let users modify the tree at runtime to set
up their own personalized "directory" structures.
Another use case in which traversal shines is when there is a need to support
a context-dependent security policy.  One example might be a document