On Apr 26, 6:07 pm, [EMAIL PROTECTED] wrote:
> I'm trying to figure out how to do something, and googling hasn't turned
> up any examples. Before I start experimenting, I thought I'd ask for
> a reality check :)
>
> I want to have URLs like this:
>
> /stream/<sid>/subobject/<oid>
>
> Here, a collection of 'subobjects' belongs to the 'stream' with id 'sid'.
> So I want to return a page displaying subobject oid. But oid is a unique
> identifier only in the _context_ of stream 'sid'.
>
> How do I do this in Turbogears? At first glance it seems like I need to
> parse all the arguments in my 'stream' controller index method (or the
> default method). There are a bunch of these URLs, though, and I don't
> want that method to be a mad agglomeration of branching code. Ideally I'd
> like to have subcontrollers for 'subobject' urls, and 'subobject2' urls,
> and be able to use all the turbogears magic goodness in writing them.
>
I wondered about this for a while, and this is what I came up with:
class ContainerController(object):
"""
This is a base class for controllers which act as containers for
resources.
It makes it easy to have URLs of the form /container/resourcename/
action
Subclasses should override the get_controller_for_resource method.
"""
def __getattr__(self, name):
if name.startswith('_cp'):
raise AttributeError(name)
cache = getattr(cp.request, '_rest_cache', None)
if not cache:
cache = cp.request._rest_cache = {}
controller = cache.get((self, name), None)
if not controller:
controller = self.get_controller_for_resource(name)
cache[self, name] = controller
if controller is None:
raise AttributeError(name)
return controller
def get_controller_for_resource(self, name):
"""
Subclasses should override this method to return an
appropriate
controller for the named resource. If the resource doesn't
exist,
the method should return None.
"""
raise NotImplemented()
Your 'stream' controller would inherit from this class and override
get_controller_for_resource to return a new controller object instance
for that specific 'sid'. It would also inherit from
ContainerController and override get_controller_for_resource to return
a controller for the 'oid'
eg (untested, probably contains typos):
# This controller would be mounted at /stream
class StreamContainer(tg.controllers.Controller, ContainerController):
def get_controller_for_resource(self, name):
# Return a StreamController for the named 'sid'
# Perhaps look up 'sid' in a database
stream = model.Stream.get(sid)
if stream:
return StreamController(stream)
def container_method(self):
"Called for /stream/container_method"
# An instance of this controller will be created on each request to
# /stream/xxx
class StreamController(tg.controllers.Controller,
ContainerController):
def __init__(self, stream):
self.stream = stream
self.subobject = ObjectContainerController(self)
def some_stream_method(self):
"Will be called for /stream/xxx/some_stream_method"
class ObjectContainerController(tg.controllers.Controller,
ContainerController):
def __init__(self, stream):
self.stream = stream
def get_controller_for_resource(self, name):
# Return an ObjectController for the subobject. Look up the
subobject
# in the db
subobject = model.Subobject.get_by(stream=self.stream,
oid=name)
if subobject:
return ObjectController(subobject)
class ObjectController(tg.controllers.Controller,
ContainerController):
def __init__(self, subobject):
self.subobject = subobject
def index(self):
"Normal index method, called for /stream/xxx/subobject/yyy/"
def some_method(self):
"another method, called for /stream/xxx/subobject/yyy/
some_method"
For your example, there are two 'container' controllers, '/stream' and
the 'subobject' property of streams. The ContainerController class is
exploiting the fact that CherryPy looks for an attribute with the name
of the next element in the path. By overriding __getattr__, we can
insert new controllers without leaving the CherryPy traversal method,
so all the normal stuff (such as automatically redirecting to 'index'
methods and so on) continues to happen. The attribute lookups are
cached so that if you have expensive DB lookups they will only be
called once per request.
Hope that helps,
Simon
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"TurboGears" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/turbogears?hl=en
-~----------~----~----~----~------~----~------~--~---