It seems like building a REST application is The Right Thing To Do. I
tend to want to create a controller for each entity in my object
model, and the rationale behind the REST paradigm gives this idea some
support.

I also appreciate the "Pylons is for HTTP and nothing more"
convention. I am, however, left to wonder how I should go about
building all the other stuff that we need from a web application, the
machinery that lives between the database and the templates in
particular. Assume for a moment that we are writing a Facebook work-
alike in Pylons. Then, a GET of http://pylonsfacebook.com/user/brianthelion
would be a request - and I'll use Facebook-speak here - to view
BrianTheLion's "profile." If we're following the REST formula that
we've argued for, then UserController.show(id=brianthelion) is getting
executed. Awesome.

At this point in the example, though, there is giant code-hole. On
Facebook, a user's profile is an extremely complicated UI. It's a
"hybrid view" (is there a better term?) of data from many different
model entities: user, event, photo, message, mail, news, etc, etc. To
build our work-alike, we need to take information about the user, the
events he's invited to, the photo's he's in, the things his friends
are saying, etc, and render all of that to fun-to-look-at HTML. So,
the context of our PylonsFacebook, the question is: How does
UserController.show() go about producing this immensely complicated
output? How should all of the code between HTTP and HTML - and there's
a lot of it; the majority, in fact - be encapsulated? How can we best
make it reusable?

SQLAlchemy aside, this territory seemed pretty lawless to me. In my
naivety I came up with the idea of "named views." For example, a GET
of http://pylonsfacebook.com/photo/981388324/icon and
http://pylonsfacebook.com/photo/981388324/full would render the "icon"
and "full res" views of that photo, respectively. Pretty self-
explanatory. This made some nice sense because then for the
PhotoController I'd have a "show_icon" method and a "show_fullres"
method, as well as a "photo.mako" with "icon" and "fullres" defs. The
rendered HTML output of the aforementioned URLs served as nice visual
building blocks for more complicated web pages, and GET-ing them
allowed me to test the code easily.

I ran into problems, though, when I started needing to build "hybrid"
views, named views that depended on other named views. For example,
http://pylonsfacebook.com/user/brianthelion/photos would call
UserController.show_photos(id=brianthelion). It's output would depend
on the output of, say, PhotoController.show_icon(id=29459285) and
PhotoController.show_icon(id=299498594). So then I'd need to
instantiate a PhotoController from within my UserController, and that
seemed - at least in MY mind - to break the Pylons paradigm.

Any thoughts? Thanks a ton for everything that has already been
offered up!

Cheers,
~br


On Jul 23, 8:17 am, Diana Clarke <[email protected]> wrote:
> I've written two pylons apps now: the first a typical web application,
> and the second a RESTful API.
>
> In both cases I started with:
>
>     paster create -t pylons helloworld
>
> followed by (for the web application):
>
>     paster controller hello
>
> or (for the RESTful API):
>
>     paster restcontroller hello hellos
>
> These code generation tools allowed me to get up and running fast, and
> made it easier to find applicable examples on the net since I was
> essentially following a set convention. I also had enough domain
> specific work to do as it was, and trusted that there were smart peeps
> behind pylons and paster, and that they had already iterated on this
> part of the stack for me.
>
> The generated code from 'restcontroller' was especially helpful -- it
> made building a RESTful API a simple paint-by-numbers exercise.
>
>     HTTP_DELETE maps to delete(): you just need to implement some
> delete resource code
>     HTTP_POST maps to create(): you just need to implement some create
> resource code
>     HTTP_PUT maps to update(): you just need to implement some update
> resource code
>     ....
>
>    http://wiki.pylonshq.com/display/pylonscookbook/How+map.resource+enab...
>
> Until I had a better idea about what I wanted from the controllers, I
> just did what the generated code comments told me to do, but at the
> same time was careful to move similar code to the BaseController
> (base.py) and use consistent form parameter naming conventions across
> resources.
>
> Eventually I had N controllers that each implemented index(), show(),
> create(), update(), and delete(). Each of these methods was only 3
> lines of code:
>
>     line 1: gather up the form parameters, or payload
>     line 2: pass them to the service layer for processing
>     line 3: serialize the response (json or XML)
>
> I've since been able to move pretty much all of this code to the
> BaseController (still just ~100 lines of code), and each of the N rest
> controllers just extend BaseController and define the service they
> should defer to for processing.
>
> So, in my case, I was able to get away with dead simple controllers: a
> couple of lines max with only an __init__() method.
>
> My controllers are now, by definition, consistent -- making it easy to
> write libraries on the flip-side to consume them.
>
>  -- show() for every resource accepts "include" options allowing you
> to ask for nested child resources in one HTTP request rather than
> making two requests (one for each resource). For example: get a folder
> and the folder's child files in one request.
>
> -- index() for every resource accepts search filters, sorting options,
> include options, paging options etc and always returns a collection
> with the resulting pagination info (page, pages, per_page, total).
>
>  etc...
>
> So, I guess my answer to what belongs in a pylons controller is almost
> *nothing*. My general rule of thumb is that I use pylons for all
> things HTTP and nothing more:
>
>     - HTTP URIs (id, format)
>     - HTTP methods (PUT, POST, GET...)
>     - HTTP routing (map.resource)
>     - HTTP form parameters (request.params.get)
>     - HTTP payload (request.body)
>     - HTTP response codes (401 Unauthorized...)
>
> The payload gets turned into an incoming DTO (data transfer object),
> the service knows what to do with it and returns an outgoing DTO, the
> DTO knows how to serialize itself based on the format.
>
>     def show(self, id, format='json'):
>
> The services I keep mentioning are in another python application
> completely -- this is to encourage the "pylons is for HTTP and nothing
> more" convention.
>
> The services are actually pretty complex in cases: persisting data to
> a cloud and databases (SQLAlchemy), firing events to RabbitMQ,
> triggering sub-actions, enforcing ACLs, validation, business logic,
> etc. In my opinion, none of this belongs in a controller.
>
> BTW, I mean no offense with: "pylons is for HTTP and nothing more".
> It's just a convention we employ for this particular project to keep
> the responsibilities clear. And it is definitely more applicable for a
> RESTful API vs. a web application with views using Mako templates for
> example. In that case, my controllers do have a bunch of tmpl_context
> assignments.
>
> Cheers,
>
> --diana

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" 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/pylons-discuss?hl=en.

Reply via email to