Phil
Phil

Reputation: 14681

Pyramid: Routing schemas and restraints

I have done my research and did some pattern matching trials but I still can not figure out how to:

  1. Make part of the route optional. e.g.:

    /required (/optional/{str}) Here the part in parentheses is optional.

  2. Apply restraints to routing so instead of a wildcard string, it has to match an item from a tuple.

    /view_1/ {('opt_a', 'opt_b', 'opt_c' ...)} in this case, if an item from the tuple is matched, it routes if not 404 or FORBIDDEN

How can I achieve these?

Thank you.

Upvotes: 5

Views: 2233

Answers (3)

Michael Merickel
Michael Merickel

Reputation: 23341

Pyramid does not support optional patterns in a route. The other answer suggests *optional but this will match much more than what you asked for which was one optional placeholder, and leaves you with no options at the end of the route for more patterns.

config.add_route('name_with_optional', '/required/{optional}/{str}')
config.add_route('name', '/required/{str}')

Now you want to use the same view for both I imagine, since you are thinking of the placeholder as optional. Thus, simply register the view for both cases:

@view_config(route_name='name_with_optional')
@view_config(route_name='name')
def my_view(request):
    optional = request.matchdict.get('optional')

The optional variable will be None if 'name' was the matched route pattern.

As for your second question, you can simply create a custom predicate. This could be either on the route or the view (remember that these are separate in Pyramid). The signature for the predicate is different in each case.

A predicate on the route (less common):

def opt_must_contain(info, request):
    opt = info['match'].get('opt')
    return opt in ('opt_a', 'opt_b', 'opt_c')

config.add_route('my_route', '/view_1/{opt}', custom_predicates=[opt_must_contain])

If this predicate returns False then another route with the same pattern could be matched (the route is ignored).

A predicate on the view (more common):

def opt_must_contain(context, request):
    opt = request.matchdict.get('opt')
    return opt in ('opt_a', 'opt_b', 'opt_c')

config.add_route('my_route', '/view_1/{opt}')

@view_config(route_name='my_route', custom_predicates=[opt_must_contain])
def my_view(request):
    opt = request.matchdict.get('opt')

In the view we'll know opt is one of the required options.

Upvotes: 13

Sergey
Sergey

Reputation: 12437

In addition to using custom route predicates, you can achieve both things using URL traversal. I wrote a detailed answer in response to your other question.

I believe traversal is a cleaner and "more natural" solution for such use cases.

Upvotes: 0

Jon Clements
Jon Clements

Reputation: 142256

It's tricky to explain, but what you want is here http://docs.pylonsproject.org/projects/pyramid/en/1.0-branch/narr/hybrid.html - it's not the easiest of reads, but explains what you're after.

Update after further research

Using:

config.add_route('name', 'foo/*optional')

it's possible to receive the remaining path elements as a tuple

Custom predicates can be used to filter url dispatch as described in http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/urldispatch.html?awesome

Upvotes: 3

Related Questions