Search code examples
restpyramidtraversalrestful-url

Pyramid traversal for PUT requests


I am trying to create a Pyramid route for a PUT request in a RESTful API to create a new resource. My application uses traversal, which works great for GET and POST:

config.add_route('myroute', '/resources/*traverse')

Since PUT should have the new resource name in the URL this obviously doesn't work with PUT since there is an unknown resource at the end so the traversal fails. I tried to create a new route for PUT using a hybrid URL dispatch and traversal approach:

config.add_route('myroute_put', '/resources/{path}/{new}', traverse='/{path}', request_method='PUT')

This works great if and only if there is only path segment to traverse. The name of the new resource is available as request.matchdict['new'] If we are at the root level, with nothing to traverse, we can still get this to work by making an auxiliary route:

config.add_route('myroute_put_root', '/resources/{new}', reqeust_method='PUT')

However, that's not a real solution because myroute_put still doesn't match if there are more then one path segments that need to be traversed, such as for the URL: /resources/path1/path2/new_resource


Solution

  • This Stack Overflow question: Pyramid traversal HTTP PUT to a URI that doesn't exist proposes a solution to create a different NewResource context type to represent new resources. The __getitem__() method of the Resource class can then always return a NewResource if it can't find the requested child. Then, a view configuration can be setup for the NewResource context and PUT request_method.

    This almost works, except by always returning a NewResource when a child isn't found instead of raising KeyError it breaks the ability to use named views as URL subordinates. For example the URL: /resources/path1/path2/my_view would mistakenly return a NewResource context for my_view instead of using that as a view_name if it exists.

    The best workaround to this problem I found so far was to create a custom Pyramid Traversal algorithm that first uses the default traversal algorithm, but then if that fails it checks if the request.method is a PUT. If so, then it returns a context of a NewResource, otherwise it returns the results of the traversal as-is.