Using Routes for Configuring Controllers

In all examples I have seen of Pylons controllers on the Web the paths to the templates being rendered have been hard-coded in the controllers. In a case where you would have different views of the same data, this could lead to people creating one controller for each template rendering the view, with all the controllers looking the same except for the final return statement. This is, of course, wasteful, and it violates the DRY principle. Could it be done differently?

In WebWork and Struts 2 the template paths and result types are configured externally (in XML), making the controllers completely unaware of what kind of view of the controller data is returned to the client. And if the controllers are mapped up as beans in a dependeny injecting container as well, they can be made pretty reusable by extracting all specificness into appropriate configuration files. But these frameworks are Java frameworks, and I am interested in Pylons controllers here.

Pylons does not include a dependency injection framework, but whatever is defined in the routings it is available to the controllers through the environ['pylons.routes_dict']. So the route mapping is a possible subject for configuring controllers. Each controller should not need to be aware of the just mentioned access to route information, so the base controller, or another supercontroller, should abstract away the source of the configuration. A simplistic example:

In config/routing.py:

map.connect('list_exploded', '/list/exploded', controller='list', action='all',
            template='exploded.tmpl')

In lib/base.py:

def __call__(self, environ, start_response):
    self.template = environ['pylons.routes_dict'].get('template')

In controllers/list.py:

class ListController(BaseController):
    def all(self):
        # ....
        return render(self.template)

The routing maps a generic «list all» action to an exploded view of all elements of something. The base controller just makes the value from the routes mapping available to the current controller, and the eventual action does not care about which template it is rendering, as long as a template path has been mapped up. A different mapping of the same controller could provide a different template path.

The solution can, of course, be made much more powerful, including more template paths for more actions, redirect paths, (semi-)constants, and similar, or just a dictionary of properties required by the controllers for them to work properly. A more advanced solution could even include mapping of result types, making the Pylons controllers completely unaware of what they return at any point. Imagination is the limit, but I would possibly find convenient a dependency injection framework with Pylons.

I would like to see more examples of Pylons controllers not hard-coding view references in the controllers, and I have outlined one possible way for doing this. I have not tried any such solution yet myself, but I probably will.

Apache Authentication Conflicting With Application Authentication

All WSGI applications I've touched so far have had test installations with extra access control. The applications have their own authentication systems restricting access to specific parts of the applications, and the overall access control to the test installations have been configured in Apache. Restricting access to a whole site in Apache is both simple and effective.

All installations have had the same problem: Apache puts the authenticated username in its REMOTE_USER variable, which is transferred to the WSGI application via Flup, and which makes the application believe that a user has been authenticated by the application. Web requests then typically fail because the application hasn't had the chance to set up the authenticated environment, or a user with the Apache username doesn't exist in the application. Luckily the fix is simple.

The WSGI applications I've worked on are loaded using Paste, passed to Flup, and managed by Apache 2 through mod_fcgid. The fcgi script with the fix is as follows:

#!/path/to/virtual/env/bin/python
config = '/path/to/config.ini'

if __name__ == '__main__':
    import logging.config
    logging.config.fileConfig(config)

    from paste.deploy import loadapp
    from flup.server.fcgi import WSGIServer
    WSGIServer(loadapp('config:' + config),
               environ={'REMOTE_USER': ''}).run()

As hightlighed in the code, an empty REMOTE_USER is passed to the Flup WSGI server, overriding the REMOTE_USER coming from Apache. The authentication solution in the application can then operate unaffected by the Apache authentication as long as it interprets an empty string as no value. Flup won't override the environment variable if you pass in None.

So now I don't have to re-figure this problem out whenever I add Apache access control to a WSGI application. It's here :)

Python Site

As mentioned earlier, I've created this site myself more or less from scratch. Here's an overview of the technologies used:

Language
Python. I've had previous blog implementations in PHP. I won't say I'm embarassed—we all need to start somewhere and PHP is a simple language. I originally went for a rewrite in Java using Spring (MVC) and Hibernate, but switched to Python after having used it for a small while at work. Python is more convenient in many ways, but I occasionally miss some of the "firmness" in Java.
Web framework
Pylons. I'll upgrade to 0.9.7 when I find the time. I haven't really looked at the changelog yet, but I like that they're moving away from star imports, at least related to the base controller. Star imports are ugly. Ever looked inside WebHelpers? *Shivers*
Templating language
Mako. I believe I'll change to Jinja2 after upgrading Pylons. We'll see. I like the inheritance blocks in Jinja better than the equivalent use of functions and function calls in Mako.
Web server
Apache 2 with mod_fcgid and flup.
Object-relational mapper
SQLAlchemy 0.4. But if you do an easy_install on it you currently get version 0.5.0 beta something. Why on earth are betas available by default? If a developer wants to use an unstable (by definition) version of a product, she should have to explicitly ask for it! Anyway...
Database engine
PostgreSQL for production and SQLite for development.
Authentication framework
Paste auth_tkt, customly wrapped for handling bad tickets. I just need something to handle the authentication cookies, and this does the job nicely.
Caching system
Memcached. I've put an abstraction layer on top of the Python Memcached client with anti-dogpiling. It's probably overkill by far on a site like this, but hey, it's fun!

The rest is just the object model, services, controllers, templates, styling, and configuration (including setting up virtual Python environments and creating deployment scripts) that makes it all into a functional web site. There are a few nifty solutions (if I might say so myself) in there which are candidate subjects for future posts.

I'm naturally keeping all code under version control. I started off with Subversion, which I've been using for quite a few years now, but have lately changed to Bazaar Version Control for this and another project, after pressure from a colleague to try it out. The only issue I have with Bazaar so far is the program name on the command line: bzr. I can't type it over and over again without straining my left arm, so I alias it to bvc (Bazaar Version Control). Have a look at your keyboard and compare b z r with b v c. The latter is obviously easier to type!

That's basically it. Don't hesitate to ask questions!