Search code examples
pythonpyramid

control order that routes are matched when using a custom directive/action


I have a catchall route that is catching my request before my call to add_route within an action created with a custom directive. How can I prevent that from happening? Ie. manually place the catchall route at the end of the route matching process or put my action's add_route at the front of the route matching process?

I tried swapping the order of the includes between admin/routes.py and routes.py but that didn't seem to have an affect. A quick solution (and probably good idea thinking about it now) is to filter admin from the pattern in the catchall. But I feel like this is going to come up again where I cannot do that so I'm asking this question.

__init__.py

def main(global_config, **settings):
    #...
    config.include('.directives')
    #...
    config.include(".routes")
    #...
    for k in (".customer", ".admin"):
        config.scan(k) # this picks up the admin/admin_routes.py

    return config.make_wsgi_app()   

admin/routes.py

def includeme(config):
    config.add_dispatched_route(
        "admin-plants-edit",
        "/admin/plants/{id}",
        'plant',
    )

routes.py


def includeme(config):
    #...
    config.add_route("page-view", "/*url")

directives.py


from pyramid.config import PHASE0_CONFIG
from pyramid.httpexceptions import HTTPNotFound

def includeme(config):
    def add_dispatched_route(config, route_name, route_pattern, dispatch_with):
        def route_factory(request):
            api = request.find_service(name=dispatch_with)
            obj = api.by_id(request.matchdict["id"])
            if not obj:
                raise HTTPNotFound()
            return obj
        def route_pregenerator(request, elements, kw):
            api = request.find_service(name=dispatch_with)
            try:
                obj = kw.pop(api.get_pregenerator_kw())
            except KeyError:
                pass
            else:
                kw["id"] = obj.id
            return elements, kw
        def register():
            config.add_route(route_name, route_pattern, factory=route_factory, pregenerator=route_pregenerator)
        config.action(('dispatched_route', route_name), register, order=PHASE0_CONFIG)

    config.add_directive('add_dispatched_route', add_dispatched_route)


Solution

  • The way the phases work in Pyramid doesn't allow you to re-order actions within a phase. Basically imagine that every call to config.add_route gets appended to a list, and then later that list is iterated in order to add the routes. You cannot inject a call to config.add_route before another call to it.

    However if you wrap every call to add_route in your own action, then you have an opportunity to defer everything, order things, and then call config.add_route with each route in the order you wish.