Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
pyramid.pdf
Скачиваний:
11
Добавлен:
24.03.2015
Размер:
3.82 Mб
Скачать

28.2. HYBRID APPLICATIONS

28.2 Hybrid Applications

Either traversal or url dispatch alone can be used to create a Pyramid application. However, it is also possible to combine the concepts of traversal and url dispatch when building an application: the result is a hybrid application. In a hybrid application, traversal is performed after a particular route has matched.

A hybrid application is a lot more like a “pure” traversal-based application than it is like a “pure” URLdispatch based application. But unlike in a “pure” traversal-based application, in a hybrid application, traversal is performed during a request after a route has already matched. This means that the URL pattern that represents the pattern argument of a route must match the PATH_INFO of a request, and after the route pattern has matched, most of the “normal” rules of traversal with respect to resource location and view lookup apply.

There are only four real differences between a purely traversal-based application and a hybrid application:

In a purely traversal based application, no routes are defined; in a hybrid application, at least one route will be defined.

In a purely traversal based application, the root object used is global, implied by the root factory provided at startup time; in a hybrid application, the root object at which traversal begins may be varied on a per-route basis.

In a purely traversal-based application, the PATH_INFO of the underlying WSGI environment is used wholesale as a traversal path; in a hybrid application, the traversal path is not the entire PATH_INFO string, but a portion of the URL determined by a matching pattern in the matched route configuration’s pattern.

In a purely traversal based application, view configurations which do not mention a route_name argument are considered during view lookup; in a hybrid application, when a route is matched, only view configurations which mention that route’s name as a route_name are considered during view lookup.

More generally, a hybrid application is a traversal-based application except:

the traversal root is chosen based on the route configuration of the route that matched instead of from the root_factory supplied during application startup configuration.

the traversal path is chosen based on the route configuration of the route that matched rather than from the PATH_INFO of a request.

the set of views that may be chosen during view lookup when a route matches are limited to those which specifically name a route_name in their configuration that is the same as the matched route’s name.

To create a hybrid mode application, use a route configuration that implies a particular root factory and which also includes a pattern argument that contains a special dynamic part: either *traverse or

*subpath.

307

28. COMBINING TRAVERSAL AND URL DISPATCH

28.2.1 The Root Object for a Route Match

A hybrid application implies that traversal is performed during a request after a route has matched. Traversal, by definition, must always begin at a root object. Therefore it’s important to know which root object will be traversed after a route has matched.

Figuring out which root object results from a particular route match is straightforward. When a route is matched:

If the route’s configuration has a factory argument which points to a root factory callable, that callable will be called to generate a root object.

If the route’s configuration does not have a factory argument, the global root factory will be called to generate a root object. The global root factory is the callable implied by the root_factory argument passed to the Configurator at application startup time.

If a root_factory argument is not provided to the Configurator at startup time, a default root factory is used. The default root factory is used to generate a root object.

latex-note.png

Root factories related to a route were explained previously within Route Factories. Both the global root factory and default root factory were explained previously within The Resource Tree.

28.2.2 Using *traverse In a Route Pattern

A hybrid application most often implies the inclusion of a route configuration that contains the special token *traverse at the end of a route’s pattern:

1 config.add_route(’home’, ’{foo}/{bar}/*traverse’)

308

28.2. HYBRID APPLICATIONS

A *traverse token at the end of the pattern in a route’s configuration implies a “remainder” capture value. When it is used, it will match the remainder of the path segments of the URL. This remainder becomes the path used to perform traversal.

latex-note.png

The *remainder route pattern syntax is explained in more detail within Route

Pattern Syntax.

A hybrid mode application relies more heavily on traversal to do resource location and view lookup than most examples indicate within URL Dispatch.

Because the pattern of the above route ends with *traverse, when this route configuration is matched during a request, Pyramid will attempt to use traversal against the root object implied by the root factory that is implied by the route’s configuration. Since no root_factory argument is explicitly specified for this route, this will either be the global root factory for the application, or the default root factory. Once traversal has found a context resource, view lookup will be invoked in almost exactly the same way it would have been invoked in a “pure” traversal-based application.

Let’s assume there is no global root factory configured in this application. The default root factory cannot be traversed: it has no useful __getitem__ method. So we’ll need to associate this route configuration with a custom root factory in order to create a useful hybrid application. To that end, let’s imagine that we’ve created a root factory that looks like so in a module named routes.py:

1

class Resource(object):

2

def __init__(self, subobjects):

3self.subobjects = subobjects

4

5def __getitem__(self, name):

6return self.subobjects[name]

7

8 root = Resource(

9{’a’: Resource({’b’: Resource({’c’: Resource({})})})}

10

)

11

12def root_factory(request):

13return root

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:

309

28. COMBINING TRAVERSAL AND URL DISPATCH

1

2

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 Resource 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.

latex-note.png

We could have also used our root_factory function as the root_factory argument of the Configurator constructor, instead of associating it with a particular route inside the route’s configuration. Every hybrid route configuration that is matched but which does not name a factory attribute will use the use global root_factory function to generate a root object.

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 resource tree in order to find a context resource, starting from the root object returned from the root factory. In the above example, the 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 against the root object will be a/b/c. As a result, 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 context resource of the view would be the Resource object we’ve named ’c’ in our bogus resource tree and the view name resulting from traversal will be the empty string; if you need a refresher about why this outcome is presumed, see

The Traversal Algorithm.

At this point, a suitable view callable will be found and invoked using view lookup as described in View Configuration, but with a caveat: in order for view lookup to work, we need to define a view configuration that will match when view lookup is invoked after a route matches:

310

28.2. HYBRID APPLICATIONS

1

2

3

config.add_route(’home’, ’{foo}/{bar}/*traverse’, factory=’mypackage.routes.root_factory’)

config.add_view(’mypackage.views.myview’, route_name=’home’)

Note that the above call to add_view() includes a route_name argument. View configurations that include a route_name argument are meant to associate a particular view declaration with a route, using the route’s name, in order to indicate that the view should only be invoked when the route matches.

Calls to add_view() may pass a route_name attribute, which refers to the value of an existing route’s name argument. In the above example, the route name is home, referring to the name of the route defined above it.

The above mypackage.views.myview view callable will be invoked when:

the route named “home” is matched

the view name resulting from traversal is the empty string.

the context resource is any object.

It is also possible to declare alternate views that may be invoked when a hybrid route is matched:

1

2

3

4

5

config.add_route(’home’, ’{foo}/{bar}/*traverse’, factory=’mypackage.routes.root_factory’)

config.add_view(’mypackage.views.myview’, route_name=’home’) config.add_view(’mypackage.views.another_view’, route_name=’home’,

name=’another’)

The add_view call for mypackage.views.another_view above names a different view and, more importantly, a different view name. The above mypackage.views.another_view view will be invoked when:

the route named “home” is matched

the view name resulting from traversal is another.

the context resource 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 resource tree, the mypackage.views.another view callable will be called instead of the mypackage.views.myview view callable because the view name will be another instead of the empty string.

More complicated matching can be composed. All arguments to route configuration statements and view configuration statements are supported in hybrid applications (such as predicate arguments).

311

28. COMBINING TRAVERSAL AND URL DISPATCH

28.2.3 Using the traverse Argument In a Route Definition

Rather than using the *traverse remainder marker in a pattern, you can use the traverse argument to the add_route() method.

When you use the *traverse remainder marker, the traversal path is limited to being the remainder segments of a request URL when a route matches. However, when you use the traverse argument or attribute, you have more control over how to compose a traversal path.

Here’s a use of the traverse pattern in a call to add_route():

1

2

config.add_route(’abc’, ’/articles/{article}/edit’, traverse=’/{article}’)

The syntax of the traverse argument is the same as it is for pattern.

If, as above, the pattern provided is /articles/{article}/edit, and the traverse argument provided is /{article}, when a request comes in that causes the route to match in such a way that the article match value is 1 (when the request URI is /articles/1/edit), the traversal path will be generated as /1. This means that the root object’s __getitem__ will be called with the name 1 during the traversal phase. If the 1 object exists, it will become the context of the request. The Traversal chapter has more information about traversal.

If the traversal path contains segment marker names which are not present in the pattern argument, a runtime error will occur. The traverse pattern should not contain segment markers that do not exist in the path.

Note that the traverse argument is ignored when attached to a route that has a *traverse remainder marker in its pattern.

Traversal will begin at the root object implied by this route (either the global root, or the object returned by the factory associated with this route).

Making Global Views Match

By default, only view configurations that mention a route_name will be found during view lookup when a route that has a *traverse in its pattern matches. You can allow views without a route_name attribute to match a route by adding the use_global_views flag to the route definition. For example, the myproject.views.bazbuz view below will be found if the route named abc below is matched and the PATH_INFO is /abc/bazbuz, even though the view configuration statement does not have the route_name="abc" attribute.

312

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]