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

8. URL DISPATCH

1 from pyramid.httpexceptions import HTTPNotFound

2 from pyramid.view import notfound_view_config, view_config

3

4 @notfound_view_config(append_slash=True)

5 def notfound(request):

6return HTTPNotFound(’Not found, bro.’)

7

8 @view_config(route_name=’noslash’) 9 def no_slash(request):

10 return Response(’No slash’)

11

12@view_config(route_name=’hasslash’)

13def has_slash(request):

14return Response(’Has slash’)

15

16def main(g, **settings):

17config = Configurator()

18config.add_route(’noslash’, ’no_slash’)

19config.add_route(’hasslash’, ’has_slash/’)

20config.scan()

latex-warning.png

You should not rely on this mechanism to redirect POST requests. The redirect of the slash-appending not found view will turn a POST request into a GET, losing any POST data in the original request.

See pyramid.view and Changing the Not Found View for for a more general description of how to configure a view and/or a not found view.

8.9 Debugging Route Matching

It’s useful to be able to take a peek under the hood when requests that enter your application arent matching your routes as you expect them to. To debug route matching, use the

PYRAMID_DEBUG_ROUTEMATCH environment variable or the pyramid.debug_routematch configuration file setting (set either to true). Details of the route matching decision for a particular request to the Pyramid application will be printed to the stderr of the console which you started the application from. For example:

86

8.10. USING A ROUTE PREFIX TO COMPOSE APPLICATIONS

1[chrism@thinko pylonsbasic]$ PYRAMID_DEBUG_ROUTEMATCH=true \

2

bin/pserve development.ini

3Starting server in PID 13586.

4 serving on 0.0.0.0:6543 view at http://127.0.0.1:6543

52010-12-16 14:45:19,956 no route matched for url \

6

http://localhost:6543/wontmatch

72010-12-16 14:45:20,010 no route matched for url \

8

http://localhost:6543/favicon.ico

92010-12-16 14:41:52,084 route matched for url \

10

http://localhost:6543/static/logo.png; \

11

route_name: ’static/’, ....

See Environment Variables and .ini File Settings for more information about how, and where to set these values.

You can also use the proutes command to see a display of all the routes configured in your application; for more information, see Displaying All Application Routes.

8.10 Using a Route Prefix to Compose Applications

latex-note.png

This feature is new as of Pyramid 1.2.

The pyramid.config.Configurator.include() method allows configuration statements to be included from separate files. See Rules for Building An Extensible Application for information about this method. Using pyramid.config.Configurator.include() allows you to build your application from small and potentially reusable components.

The pyramid.config.Configurator.include() method accepts an argument named route_prefix which can be useful to authors of URL-dispatch-based applications. If route_prefix is supplied to the include method, it must be a string. This string represents a route prefix that will be prepended to all route patterns added by the included configuration. Any calls to pyramid.config.Configurator.add_route() within the included callable will have their pattern prefixed with the value of route_prefix. This can be used to help mount a set of routes at a different location than the included callable’s author intended while still maintaining the same route names. For example:

87

8. URL DISPATCH

1 from pyramid.config import Configurator

2

3 def users_include(config):

4config.add_route(’show_users’, ’/show’)

5

6 def main(global_config, **settings):

7config = Configurator()

8config.include(users_include, route_prefix=’/users’)

In the above configuration, the show_users route will have

an effective route pattern of

/users/show,

instead of /show because the route_prefix argument will be prepended

to the pattern.

The route will then only match if the URL

path is /users/show, and

when the pyramid.request.Request.route_url() function is called with the route name show_users, it will generate a URL with that same path.

Route prefixes are recursive, so if a callable executed via an include itself turns around and includes another callable, the second-level route prefix will be prepended with the first:

1 from pyramid.config import Configurator

2

3 def timing_include(config):

4config.add_route(’show_times’, /times’)

5

6 def users_include(config):

7config.add_route(’show_users’, ’/show’)

8config.include(timing_include, route_prefix=’/timing’)

9

10def main(global_config, **settings):

11config = Configurator()

12config.include(users_include, route_prefix=’/users’)

In the above configuration, the show_users route will still have an effective route pattern of /users/show. The show_times route however, will have an effective pattern of

/users/timing/show_times.

Route prefixes have no impact on the requirement that the set of route names in any given Pyramid configuration must be entirely unique. If you compose your URL dispatch application out of many small subapplications using pyramid.config.Configurator.include(), it’s wise to use a dotted name for your route names, so they’ll be unlikely to conflict with other packages that may be added in the future. For example:

88

8.11. CUSTOM ROUTE PREDICATES

1 from pyramid.config import Configurator

2

3 def timing_include(config):

4config.add_route(’timing.show_times’, /times’)

5

6 def users_include(config):

7config.add_route(’users.show_users’, ’/show’)

8config.include(timing_include, route_prefix=’/timing’)

9

10def main(global_config, **settings):

11config = Configurator()

12config.include(users_include, route_prefix=’/users’)

8.11 Custom Route Predicates

Each of the predicate callables fed to the custom_predicates argument of add_route() must be a callable accepting two arguments. The first argument passed to a custom predicate is a dictionary conventionally named info. The second argument is the current request object.

The info dictionary has a number of contained values: match is a dictionary: it represents the arguments matched in the URL by the route. route is an object representing the route which was matched (see pyramid.interfaces.IRoute for the API of such a route object).

info[’match’] is useful when predicates need access to the route match. For example:

1 def any_of(segment_name, *allowed):

2def predicate(info, request):

3if info[’match’][segment_name] in allowed:

4

return True

5return predicate

6

 

7

num_one_two_or_three = any_of(’num’, ’one’, ’two’, ’three’)

8

 

9

config.add_route(’route_to_num’, ’/{num}’,

10

custom_predicates=(num_one_two_or_three,))

The above any_of function generates a predicate which ensures that the match value named segment_name is in the set of allowable values represented by allowed. We use this any_of function to generate a predicate function named num_one_two_or_three, which ensures that the num segment is one of the values one, two, or three , and use the result as a custom predicate by feeding it inside a tuple to the custom_predicates argument to add_route().

89

8. URL DISPATCH

A custom route predicate may also modify the match dictionary. For instance, a predicate might do some type conversion of values:

1def integers(*segment_names):

2 def predicate(info, request):

3match = info[’match’]

4for segment_name in segment_names:

5

try:

6

match[segment_name] = int(match[segment_name])

7

except (TypeError, ValueError):

8

pass

9

return True

10

return predicate

11

 

12

ymd_to_int = integers(’year’, ’month’, ’day’)

13

 

14

config.add_route(’ymd’, ’/{year}/{month}/{day}’,

15

custom_predicates=(ymd_to_int,))

Note that a conversion predicate is still a predicate so it must return True or False; a predicate that does only conversion, such as the one we demonstrate above should unconditionally return True.

To avoid the try/except uncertainty, the route pattern can contain regular expressions specifying requirements for that marker. For instance:

1def integers(*segment_names):

2 def predicate(info, request):

3match = info[’match’]

4for segment_name in segment_names:

5

match[segment_name] = int(match[segment_name])

6return True

7return predicate

8

 

9

ymd_to_int = integers(’year’, ’month’, ’day’)

10

 

11

config.add_route(’ymd’, ’/{year:\d+}/{month:\d+}/{day:\d+}’,

12

custom_predicates=(ymd_to_int,))

Now the try/except is no longer needed because the route will not match at all unless these markers match \d+ which requires them to be valid digits for an int type conversion.

The match dictionary passed within info to each predicate attached to a route will be the same dictionary. Therefore, when registering a custom predicate which modifies the match dict, the code registering

90

8.11. CUSTOM ROUTE PREDICATES

the predicate should usually arrange for the predicate to be the last custom predicate in the custom predicate list. Otherwise, custom predicates which fire subsequent to the predicate which performs the match modification will receive the modified match dictionary.

latex-warning.png

It is a poor idea to rely on ordering of custom predicates to build a conversion pipeline, where one predicate depends on the side effect of another. For instance, it’s a poor idea to register two custom predicates, one which handles conversion of a value to an int, the next which handles conversion of that integer to some custom object. Just do all that in a single custom predicate.

The route object in the info dict is an object that has two useful attributes: name and pattern. The name attribute is the route name. The pattern attribute is the route pattern. An example of using the route in a set of route predicates:

1def twenty_ten(info, request):

2 if info[’route’].name in (’ymd’, ’ym’, ’y’):

3return info[’match’][’year’] == ’2010’

4

5config.add_route(’y’, ’/{year}’, custom_predicates=(twenty_ten,))

6 config.add_route(’ym’, ’/{year}/{month}’, custom_predicates=(twenty_ten,))

7config.add_route(’ymd’, ’/{year}/{month}/{day}’,

8

custom_predicates=(twenty_ten,))

The above predicate, when added to a number of route configurations ensures that the year match argument is ‘2010’ if and only if the route name is ‘ymd’, ‘ym’, or ‘y’.

You can also caption the predicates by setting the __text__ attribute. This will help you with the pviews command (see Displaying All Application Routes) and the pyramid_debugtoolbar.

If a predicate is a class just add __text__ property in a standard manner.

1 class DummyCustomPredicate1(object):

2def __init__(self):

3self.__text__ = ’my custom class predicate’

4

 

5

class DummyCustomPredicate2(object):

6

__text__ = ’my custom class predicate’

91

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