5

I'm having a problem wrapping my head around my Pyramid web application. I have it structured very much like the one described by Michael Merickel here, except I'm using pure traversal to find my views. (They are declared configured with context='path.toResource' name='myView'), pretty standard fare according to what I can tell from the traversal wiki tutorial. My application has a more complex URL structure though: My User resources are under /users/{user_id} and my projects are under /projects/{project_id}. All of my resources are persisted using the SQLAlchemy ORM; I have a User and Project class with __name__ and __parent__ attributes, as well as other attributes extending Columns.

class User(Base):
    id = Column(...)
    __name__ = None
    __parent__ = None
Class Project(Base):
    id = Column(...)
    __name__ = None
    __parent__ = None
    owner_id = Column(...ForeignKey(User.id))
    owner = relationship(User,...)

I have a RootFactory, ProjectFactory and UserFactory that populate the appropriate __name__ and __parent__ attributes in their __get_item__ calls.

So, in the view functions for the Project context, I get a Project instance in request.context. My issue is how the heck do I reference the corresponding User instance? I can't do project.owner, because that User instance didn't go through the RootFactory chain, so its __parent__ and __name__ values are unset. This is bad, because I want to use request.resource_url to find the URL for the owner User, so I can put a link on the view page.

What's the solution here SO? Do I do everything through request.root? What if I want to make a complex query that returns User or Project instances? Is there some kind of CrapFactory I can pass SQLAlchemy so all its instances get populated properly?

Is my approach just completely wrong?

I feel like I wouldn't be having these kinds of problems if I just stuck with URL Routing...

Community
  • 1
  • 1
frank
  • 1,322
  • 1
  • 10
  • 28

1 Answers1

5

This is the issue everyone runs into when trying to mix SQLAlchemy with traversal. A requirement of traversal is having a persistent tree structure that you can traverse to get from one node to another.

You basically have 2 options.

  1. You can build an actual tree in your database where each resource knows where it is located. Thus when you load a resource it has some way of determining its parent and name. This is the best option. This is commonly done by creating a self-referential resource table in your database. Then your User and Project objects would both have resource_id foreign keys that they could use to determine where they are at within the tree.

  2. Your name and parent are currently only populated when you traverse the tree from the root, so you can do everything through request.root in order to reference other parts of the tree.

It is also possible to just define a __resource_url__ property on each resource but this is fairly hackish as well for most cases.

The thing to take away is that there is a big difference from saying "my urls have this structure" and actually building a persistent hierarchy of that structure that you can use. For handling URLs it's fairly simple, but for generating them it can be painful without a persistent tree.

Michael Merickel
  • 23,153
  • 3
  • 54
  • 70
  • I was hoping for some way to decouple the URL representation of my resources from the resources themselves... It would be nice if I could just declare some factory be the container for all instances of a particular type, and have Pyramid handle the rest. Oh well... – frank Dec 08 '11 at 18:24
  • The definition of traversal is that the resources being traversed are coupled to the URL representation. – Michael Merickel Dec 08 '11 at 18:27
  • You can of course wrap your "model" objects in "resource" objects that actually handle URL stuff, but you will still run into your current problem of not having a persistent tree somewhere. This is an incredibly common problem with people who try traversal. There needs to be a persistent tree somewhere or generating URLs/resources is going to be painful. – Michael Merickel Dec 08 '11 at 18:29
  • Am I stuck with using the root if I want the request to determine the resource parent? I'd like my Projects to be able to be accessed from /project/{id} as well as /p/{short_url} – frank Dec 09 '11 at 15:54
  • This is the point of the persistent tree. When your resource is tied to its parent (instead of being generated by traversal) then you can just load the resource and its parent from the database without caring where in the tree it is. Making a relocatable resource is outside the scope of this SO question and is not a topic I'm going to answer in a comment box. – Michael Merickel Dec 12 '11 at 19:57
  • Thank you! Even though it's outside the scope you've armed me with the jargon/vocabulary I needed to ask the question I was meaning to. – frank Dec 13 '11 at 09:33
  • I've had the same problem using traversal and SQLAlchemy. It's unfortunate because it seems like overkill to have to persist all resources. Sadly, the very static '/' and '/users' resources need to be persisted. The traversal with sqlalchemy setup requires an all-or-nothing approach. No hybrid persisted and non-persisted resources unless you want to hack it. I'm already in half way. I guess I'll likely need to go all in now. :-/ – lostdorje Jan 18 '12 at 12:14
  • Pyramid fully supports approaches that use traversal for part of your site and dispatch for other parts. They are not mutually exclusive. – Michael Merickel Jan 18 '12 at 16:25