1

I'm getting started with Pyramid, and am excited about traversal as a routing strategy. It seems natural and powerful. However, my thinking has led me down a questionable path. Can you suggest a better pattern?

I'm trying to implement a few RESTful routes. For example, I'd like to map the following:

  • GET /users -> index
  • GET /users/chris -> show
  • GET /users/new -> new
  • POST /users -> create
  • PUT /users/chris -> update
  • DELETE /users/chris -> destroy

It seems like the final context of the show and update actions ought to be the appropriate instance of User, but what about index and create? It seems like the User class itself would be an appropriate context, however the traversal mechanism, the __getitem__ method, can't be implemented on classes without some metaclass magic. It seemed like there might be a cleaner way to do this. Obviously I could create a UserResourceContainer whose only job would be to handle traversal, but I'm attracted to the elegance of using the User class. Thoughts?

Manuel Allenspach
  • 12,467
  • 14
  • 54
  • 76
cproctor
  • 175
  • 8
  • How do you plan to implement the /users resource without describing it as some sort of container? That seems like the most obvious solution to me. As far as "metaclass magic" goes I'm afraid I don't follow, you can add a `__getitem__` method to any class definition, you just can't add it to an object instance dynamically without some trickery, but that's rare that you would want to do that. – Michael Merickel Oct 16 '13 at 18:32
  • I was considering using the User class (as a singleton) as the container. The difficulty is that implementing __getitem__ as a class method (with the @classmethod decorator) is [not trivial](http://stackoverflow.com/questions/6406519/iterating-class-object). – cproctor Oct 19 '13 at 18:38

1 Answers1

1

Don't forget that you need a context for the views that map to /users. Having a single User class would make it quite difficult, and certainly not elegant.

My guess is that a separate UserContainer would be the simpler solution you can find. Depending on you database backend, you might even be able to do it in a generic way. For example, with SqlAlchemy:

class Container:
    def __getitem__(self, item):
        try:
            id = int(item)
            return DBSession.query(self.cls).filter_by(id=id).one()
        except (NoResultFound, ValueError):
            raise KeyError(item)

Then, your UserContainer is just:

class UserContainer(Container):
    cls = User

Elegant, isn't it ?

madjar
  • 12,691
  • 2
  • 44
  • 52
  • Yeah, I'll go with a separate container. Coming from MVC, I found the separation of model logic from its use in the webapp a familiar pattern. I guess I was just intrigued by the possibility of the models fully supporting traversal on their own. After all, your Container's __getitem__ is very similar to a `User.find({'name': 'chris'})` method one might implement as a class method. That is a neat trick. I'm using a Mongo backend, but I can still use your idea with a little modification. Thanks! – cproctor Oct 19 '13 at 18:41