Search code examples
pythonurl-routingpyramidtraversaldom-traversal

Pyramid Traversal __name__ matching a view name


In a Traversal pyramid app, how does one handle a resource w/ a __name__ that matches a view's name?

If I wanted to get to the view callable "view" for a Resource, I'd use a URL path like:/foo/bar/view. It traverses the resource_tree as so:

RootFactory(request) => RootFactory
RootFactory['foo']   => Foo
Foo['bar']           => Bar
Bar['view']          => KeyError

...and because it can't traverse past Bar & 'view' is left over, it assumes that 'view' is the view name, and matches to my view callable

@view_config(name='view')
def view(context, request):
    return Response(context.__name__)

To get the URL for that path I'd use request.resource_url(resource, "view").

However, if I had a resource such that Bar.__name__ = "view", how can I resolve a URL for "view" on Foo?

# path: /foo/view
RootFactory(request) => RootFactory
RootFactory['foo']   => Foo  # ideally stop here with view_name="view"
Foo['view']          => Bar.__name__ = "view"
# all parts of path matched so view_name="" and context=Bar

Ideally, in this situation, /foo/view would point to view(Foo, request), and /foo/view/view would point to view(Bar, request) where Bar.__name__ == "view".

Is there a way to handle this without writing detection for collisions between __name__ and view names?


Solution

  • The solution I wound up at is adding an arbitrary layer of container resources.

    Below is an example structure of the resources:

    class Foo(object):
    
        def __init__(self, parent, name):
            self.__parent__ = parent
            self.__name__ = name
    
        def __getitem__(self, key):
            if key == "bar":
                return BarContainer(self, "bar")
            else:
                raise KeyError()
    
    class BarContainer(object):
    
        def __init__(self, parent, name):
            self.__parent__ = parent
            self.__name__ = name
    
        def __getitem__(self, key):
            return Bar(self, key)
    
    class Bar(object):
    
        def __init__(self, parent, name):
            self.__parent__ = parent
            self.__name__ = name
    

    The question mentions wanting to generate resource URLs to the view callable titled view. With BarContainer this just adds an extra slash:

    /{foo}/bar/{bar}/<view_name>    
    /a/view          => view(context=Foo(name='a'), request)
    /a/bar/view      => view(context=BarContainer(name='bar'), request)
    /a/bar/b/view    => view(context=Bar(name='b'), request)