views:

227

answers:

2

I'm writing a cherrypy app and I was wondering what the best way is for structuring my handlers and code for larger applications?

I realize assignment is simple trough cherrypy.root, but what are some practices for writing the handlers and assigning them?

(Allow me to prove my confusion!) My initial thought is to write a standard handler class that infers a template to run based on the current URL or class/method combination. Then I would assign one instance of that handler multiple times to the path to create pages. I don't see this working however as the recursive references wouldn't work quite right.

So, given the fact that I'm already drawing blanks on how my own source code should look, I'd love some pointers and examples!

Feel free to ask some detailed questions for me to clarify. While there is plenty of cherrypy tutorial material out there, it tends to only scratch the surface.

+1  A: 
David Morrissey
I'd love to hear your views behind my approach so far to making an MVC: I'm writing a default class that I will always specify as my root. It will have a default(self, *args, **kwargs) method exposed. This way, whenever a request isn't handled, I can try and dynamically instantiate a "controller" class and then following that, trigger a template with the results from the controller. If this strikes you as at all "rails like", it's definitely similar. I've taken this approach with PHP with good results in the past. Although nothing quite so promising as what I can do with cherrypy!
Omega
In my opinion, if there is just static methods it's better to just use `@expose()` before the function or `function.exposed = True` after the function, depending on which you prefer (I like the latter :-). But for more complicated stuff like Wiki pages with lots of dynamic pages, then using a `default()` fallback will work well, and is perfectly acceptable. Remember to add handling for 404 not found errors etc, with `raise ObjectDoesNotExist` or create your own error page and go `cherrypy.response.status = 404` before returning.
David Morrissey
Sorry, `ObjectDoesNotExist` is for Django, it's `raise cherrypy.HTTPError(404)` for cherrypy (http://www.cherrypy.org/wiki/ErrorsAndExceptions)
David Morrissey
+2  A: 

CherryPy deliberately doesn't require you to subclass from a framework-provided base class so that you are free to design your own inheritance mechanism, or, more importantly, use none at all. You are certainly free to define your own base class and inherit from it; in this way, you can standardize handler construction and configuration via the __init__ method of your class, and via class-level variables and methods.

However, the preferred approach is different. For most web applications, you don't really want to vary the actual construction logic of your handlers, nor do you care much about class-level variables or methods; instead, you want reusable variables and methods per URI or per subtree of URI's or per site, not per class. You tend to vary one set of handlers from another set more by instance configuration (handler metadata) and instance methods (handler logic). Traditional class-based inheritance can do this, but it's a bit of a blunt instrument for that kind of customization.

CherryPy, therefore, is designed to provide this sort of per-resource-set customization that class-based inheritance doesn't do well. It provides this through 1) the design of its configuration system, which allows you to bind metadata to a single URI, a subtree of URI's, a subtree of handlers, or a whole site with the same syntax (see http://docs.cherrypy.org/dev/intro/concepts/config.html for an overview), and 2) the hooks and tools system, which allows you to bind logic to a single URI, a subtree of URI's, a subtree of handlers, or a whole site. See http://docs.cherrypy.org/dev/intro/concepts/tools.html

So, practically: do use normal attributes on cherrypy.root to build your tree of handlers:

def make_app():
    root = Root()
    root.foo = Foo()
    root.bars = BarCollection()
    return root

However, don't make Root, Foo and Bar inherit from a common base class. Instead, write independent Tools to do things like "infer templates". That is, instead of:

from cherrypy import expose

class Foo(MyAppBase):
    @expose()
    def index(self, a, b, c):
        ...

root.foo = Foo(template='foo.html')

write:

from cherrypy import expose, tools

class Foo(object):
    @tools.render(template='foo.html')
    @expose()
    def index(self, a, b, c):
        ...

root.foo = Foo()

...where 'tools.render' is a CherryPy Tool you have written to look up and apply the given template. This approach will allow you to override the arguments to the Tool in your config file and avoid having to repackage or patch your code:

[/foo/]
tools.render.template = 'foo2.html'
fumanchu