Fun with Django and modwsgi


Today I deployed my first Django application for a client. Its yet-another-blog, so I’ll refrain from posting the code and cluttering up the django-*blog* namespace on Google Code. Before you roll your eyes and complain about why I didn’t use an existing solution, I think I have 2 somewhat valid reasons:

  1. The client actually needed a _sub_set of the features most blogs offer, so I wouldn’t really have anything to contribute back to an existing project.
  2. Blogs are one of the simplest content driven web applications in existence. Wikis are just a bit simpler perhaps. At any rate, creating a blog app is an excellent way to learn a framework.

Python Deployment Decisions

In the past I’ve used CherryPy as my framework and a simple mod_proxy configuration to run the applications behind Apache. Django considers its built-in web server a development tool only, so I figured it was time to explore the myriad of Python web app deployment alternatives: mod_python, FastCGI, modwsgi. I’m sure there are many more, but I’d say those are the big 3.

I had tried to deploy Python web applications on DreamHost using FastCGI before and entered the hell that is deploying Python web apps on shared hosts. So FastCGI wasn’t my first choice this time.

I had also tried mod_python for deploying CherryPy apps on my Linode before and for whatever reason just found mod_proxy to be much easier to setup and manage.

I was kind of eager to try out modwsgi because its been getting a lot of attention lately, so I downloaded the source and compiled it on my Debian Etch server.

Deploying a Django App via modwsgi

modwsgi was quite easy to setup as long as you follow the instructions in their wiki for Django integration. I was hit by bug #3762, but the modwsgi documentation got me through it. (For what its worth the attached wsgi.patch also worked, but I don’t really want to run a patched version of Django.)

One big problem I ran into was sqlite3 gave me OperationalError: unable to open database file whenever I did anything that would write to the database. My database file was owned by www-data (the Apache process owner) and had the permissions 664.

I switched to PostgreSQL, ran syncdb, and everything worked beautifully.

My wsgi script file /srv/spam/eggs/eggs.wsgi:

import os, sys
os.environ['DJANGO_SETTINGS_MODULE'] = 'eggs.wsgi_settings'

import django.core.handlers.wsgi

_application = django.core.handlers.wsgi.WSGIHandler()

def application(environ, start_response):
    environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
    return _application(environ, start_response)

Note I use wsgi_settings instead of my usual settings file. wsgi_settings just imports my main settings file and changes some to their production values.

My Django application actually drops into the /blog/ and /accounts/ folders under a VirtualHost otherwise occupied by static files and some PHP scripts. modwsgi made this easy by putting this in my existing VirtualHost:

WSGIScriptAliasMatch /(blog|accounts)/.* /srv/spam/eggs/eggs.wsgi

# A simple Alias directive handles my static files
Alias /static/ /srv/spam/eggs/static/

Bottom Line

I highly recommend using modwsgi for deploying Python web applications. sqlite3 may work for you. In my case its probably best I use PostgreSQL for a number of reasons.