F. Oliver Gathmann wrote:
> Hello!
> 
> Coming from Pylons, I'm a newbie to the object graph traversal world, so 
> this question might be slightly misplaced (but hopefully a no-brainer 
> for the bfg gurus...).
> 
> I very much like the concept of looking up my model object before 
> dispatching to a view that operates on it. However, what bothers me 
> about the standard traversal algorithm is that, for a URL like
> 
> /cars/beetle/wheels/front_left
> 
> I have to promote the containers ("cars" and "wheels") to first class 
> citizens in my model (by defining CarContainer and WheelContainer 
> classes that I can then instantiate and return as the context object 
> from the parent's __getitem__), where in fact they are just collections 
> held by their mutual parent model objects (the Root object in case of 
> the cars collection - which is in itself artificial, but I'm willing to 
> pay that price for traversability - and the Car object in case of the 
> wheels collection).

Great question.

The short answer is "yes, you have to define the intermediate artificial 
objects if you want to use traversal".

There are number of ways to conceptualize this.

The first way would be to not consider this a traversal problem at all and do 
it as you might in Pylons by registering a route that had static parts and 
dynamic parts:

<route
   name="wheel"
   path="/cars/:type/wheels/:wheel"
   view=".views.wheel_view/>

This will do more or less what you expect, but your view will need to resolve 
the dynamic bits from your model.

The paster template closest to this model is "bfg_routesalchemy".

Another way to do it would be to use *only* traversal and define an object 
graph:

class Root(dict):
      def __getitem__(self, name):
          return self[name]

class Cars(object):
     def __getitem__(self, name):
         .... use the rdb to find the car model and return a Car ...


class Car(object):
     def __getitem__(self, name):
         .... use the rdb to find the part (such as a Wheel) and return it...

class Wheel(object):
     # leaf node
     pass

model_graph = Root({'cars':Cars()})

def get_root(request):
     return model_graph

Then define a view like so:

<view
    view=".views.wheel_view"
    for=".models.Wheel"
    />

Then the URL to a wheel model might be:

/cars/beetle/front_wheel

You can choose to inject a "wheels" collection in there too should you want, 
but since objects have a type in this setup, it's not strictly necessary.

The paster template closest to this model is "bfg_alchemy" (or "bfg_zodb" but 
that uses ZODB rather than SQL).

Those are the two sanest ways to do this.  There's another mode that combines 
both traversal and url dispatch (routes), but I don't think it helps much here.

Malthe suggested that you can also use request.subpath.  This is the 
"remainder" of the path (as a tuple) when a traversal falls off the graph. 
Another (much cruder) way to getting this done would be to one root model and a 
single view and use request.subpath as a cue to that view about "what needs to 
be done".  You could of course have a "cars" model that resolved a car and a 
view against the car, then use the remainder of the subpath to figure out which 
part to show; dialing use of request.subpath up or down in length.

> 
> I guess I'm asking if there is a standard, "bfg-approved" way of 
> avoiding artificial container model classes - or did I just "not get it" 
> yet?!

No, you got it.

- C

_______________________________________________
Repoze-dev mailing list
Repoze-dev@lists.repoze.org
http://lists.repoze.org/listinfo/repoze-dev

Reply via email to