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?
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)