views:

275

answers:

2

Notes: Cannot use Javascript or iframes. In fact I can't trust the client browser to do just about anything but the ultra basics.

I'm rebuilding a legacy PHP4 app as a MVC application, with most of my research currently focused with the Pylon's framework.

One of the first weird issues I've run into and one I've solved in the past by using iframes or better yet javascript is displaying a dynamic collection of "widgets" that are like digest views of a typical controller's index view.

Best way to visualize my problem would be to look at Google's personalized homepage. They solve the problem with Javascript, but for my scenario javascript and pretty much anything above basic XHTML is not possible.

One idea I started working on was to have my Frontpage controller poll a database or other service for the currently activated widgets, then taking a list of tuples/dicts, dynamically instantiate each controller and build a list/dict of render sub-views and pass that to the frontpage view and let it figure things out.

So with peusudo code:

Get request goes to WSGI
WSGI calls pylons
Pylons routes to Frontpage.index()
Frontpage.index() 
   myViews = list()
   for WidgetController in ActiveWidegets():
        myViews.append(subRender(WidgetController, widgetView))

c.subviews = myViews
render(frontpage.mako)

Weird bits about subRender

  • Dynamically imports controllers via __import__ (currently hardcoded to project's namespace :( )
  • Has a potential to be very expensive (most widget calls can be cached, but one is a user panel)

I feel like there has to be a better way or perhaps a mechanism already implemented in WSGI or better yet Pylons to do this, but so far the closest I've found is this utility method: http://www.pylonshq.com/docs/en/0.9.7/modules/controllers_util/#pylons.controllers.util.forward but it seems a little crazy to build N instances of pylons on top of pylons just to get a collection views.

A: 

You could use ToscaWidgets to encapsulate your widgets, along with a stored list of the widgets enabled for each user (in database or other service, as you suggest). Pass a list of the enabled ToscaWidgets to the view and the widgets will render themselves (including dynamically adding CSS/JavaScript references to the page if widget requires those resources).

Chris Miles
@Chris - I started looking into Tosca recently actually, I was under the impression that it was more a form builder tool or am I mistaken?
David
@David - no, it isn't form specific although it provides a lot of form-based widgets in tw.forms. TW itself is a "widget" encapsulation library that can wrap any re-usable UI component. Docs possibly don't make it obvious.
Chris Miles
+6  A: 

While in most cases I'd recommend what you originally stated, using Javascript to load each widget, since that isn't an option I think you'll need to do something a little different.

In addition to using the approach of trying to have a single front controller go through all the widgets needed and building them, an alternative you might want to consider is making more powerful use of the templating in Mako.

You can actually define small blocks as Mako def's, which of course have full Python power. To avoid polluting your Mako templates with domain logic, make sure to keep that all in your models, and just make calls to the model instances in the Mako def's as needed for that component of the page to build itself.

A huge advantage of this approach is that since Mako def's support cache args, you can actually have components of the page decide how to cache themselves. Maybe the sidebar should be cached for 5 mins, but the top bar changes every hit for example. Also, since the component is triggering the db hit, you'll save db hits when the component caches itself.

ToscaWidgets doesn't have the performance to make it a very feasible option on a larger scale, so I'd stay away from trying that out.

As for some tweaks to your existing idea, make sure not to actually use Pylons controllers for 'widgets', as they do much more as needed to support WSGI that you don't need for building a page up of widgets.

I'd consider having all Widget classes work like so:

class Widget(object):
    def process(self):
        # Determine if this widget should process a POST aimed at it
        # ie, one of the POST args is a widget id indicating the widget
        # to handle the POST

    def prepare(self):
        # Load data from the database if needed in prep for the render

    def render(self):
        # return the rendered content

    def __call__(self):
        self.process()
        self.prepare()
        return self.render()

Then just have your main Mako template iterate through the widget instances, and call them to render them out.

Ben Bangert