Django 1.5 on Webfaction

The setup of a Django application on Webfaction is really easy using their one-click-installer. But this setup is really static and surely not the best option to use for Django. Luke Plant wrote about setting up Django with gunicorn on Webfaction in his blog, I will write about setting up Django 1.5 with Apache and mod_wsgi without the Django one-click-installer but the mod_wsgi one (mod_wsgi 3.4 / Python 2.7). The setup won't be the fastest Django setup in the world, but it will work.
It's basically an update for my old blog post covering Django 1.3:

Set up a mod_wsgi application and create a website in the Webfaction control panel. Log into shell and goto your newly created app. Create a virtualenv and a folder for your project files (maybe you want to check out from a VCS), activate the virtualenv and install Django an whatever you need. Edit the default wsgi.py create by the django-admin.py startproject command:

"""
WSGI config for tools_project project.

This module contains the WSGI application used by Django's development server
and any production WSGI deployments. It should expose a module-level variable
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
this application via the ``WSGI_APPLICATION`` setting.

Usually you will have the standard Django WSGI application here, but it also
might make sense to replace the whole Django WSGI application with a custom one
that later delegates to the Django one. For example, you could introduce WSGI
middleware here, or combine a Django application with an application of another
framework.

"""
import os

# set paths correctly on webfaction
if os.environ['STAGE'] == 'webfaction':
    import site
    import sys

    site.addsitedir('/home/<username>/webapps/<app>/<virtualenv-folder>/lib/python2.7/site-packages')
    sys.path.append('/home/<username>/webapps/<app>/project')

# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use
# os.environ["DJANGO_SETTINGS_MODULE"] = "tools_project.settings"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tools_project.settings")

# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)

You can leave out the check of the STAGE environment variable if you haven't set it. I use it to run a project on different hosts and identify each host.

Now adjust the Apache2 config by editing /home/<username>/webapps/<app>/apache2/conf/httpd.conf. Remove the following lines on the bottom:

<Directory /home/<username>/webapps/<app>/htdocs>
    AddHandler wsgi-script.py
</Directory>

Instead, add the following line that tell the WSGI daemon to run the wsgi.py of Django:

WSGIScriptAlias / /home/<username>/webapps/<app>/project/<projectname>/wsgi.py

Then restart the Apache and you're done.

Serving static files for Django on Webfaction

In my last post I wrote about the setup of Django with virtualenv on the Webfaction hosts. The post did not cover the serving of static files and will be covered here. It's always better to serve static media through an extra HTTP server/container doing nothing but serving static media files for better scaling. Normally I use nginx for this as it is small and fast. For Webfaction hosting you have two possibilities:

You can add a "Static only" application that is an nginx, configure it for a separate (sub)domain and then configure your Django project to use this domain for serving the files. Wasn't that easy!

Or you can use the Apache for serving the files.

Create the "media" folder somewhere on your server (e.g. within the virtualenv folder: /home/<username>/webapps/<wsgi-app>/<virtualenv-dir>/media). Now adjust the apache.conf to load the modules required for serving the media and set some expiration time if you like (I used 12 hours as seen below). I'm only writing the updates to the apache.conf of the last post:

...
# put after other LoadModule lines
LoadModule alias_module      modules/mod_alias.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule expires_module    modules/mod_expires.so

...

<VirtualHost *:1234>
    ...
    # after WSGIScriptAlias
    Alias /media /home/your_user_name/webapps/your_wsgi_app_name/virtualenv_directory/media/
    <Directory /home/your_user_name/webapps/your_wsgi_app_name/virtualenv_directory/media>
        Order allow,deny
        Allow from all
        ExpiresActive On
        ExpiresDefault "modification plus 12 hours"
    </Directory>
    ...
</VirtualHost>

Django with virtualenv on Webfaction

I really like the Django web framework and I like virtualenv enabling me to use different python configs and packages for different projects. And I have to say I like Webfaction, so easy, so good! (No, I don't get paid by them) This is a quick guide on how to setup Django in virtualenv using mod_wsgi with a Webfaction account.

  • Create an "mod_wsgi" application and create a website to use this application
  • connect to your accounts shell using ssh
  • enable Python 2.6.x as default Python interpreter (as written in the Webfaction documentation)

echo "alias python=python2.6" >> ~/.bash_profile
source ~/.bash_profile
python -V
  • install "pip" and "virtualenv" (be sure to use easy_install-2.6 to use the setuptools of Python 2.6.x):

easy_install-2.6 -U pip
easy_install-2.6 -U virtualenv
  • I like yolk, that enables you to list the installed Python packages, install it using pip if you like

pip install yolk
  • Note that these packages (pip, virtualenv, yolk) are installed in your global Python. I recommend to not install any more packages to the global installation as virtualenv enables use to install all packages we need into the virtualenv Python lib.
  • navigate to your recently create mod_wsgi webapp, there should be 2 folders, "apache2" and "htdocs"
  • create the virtualenv for your project in the webapp folder, name it like you want, I'll use "ve" here

virtualenv --no-site-packages --distribute ve
  • a new folder "ve" with the virtualenv was created. Please refer to the virtualenv documentation for more information and usage of virtualenv.
  • Now let's install yolk into the virtualenv to see which packages are in there
    • either activate the virtualenv and install or use pip magic to install into an virtualenv without activating it:

source ve/bin/activate
pip install yolk
// or
pip -E ve install yolk
  • now you activate the virtualenv and execute yolk:

source ve/bin/activate
yolk -l
deactivate
  • Install Django and any requirements the same way as demonstrated for yolk.
  • Assuming you installed Django into the virtualenv and created a Django project, we now must adjust the Apache config
    • backup the httpd.conf from /webapps/<yourdjangoapp>/apache2/conf
    • create a django.wsgi file somewhere in your webapp directory (I use the conf folder of apache)
    • scan the httpd.conf for "Listen XXX", XXX ist the webapp port
    • remove the <Directory>...</Directory> segment
    • append the following telling the Apache to use the wsgi config we create in the next step:

NameVirtualHost *:<webapp-port>
<VirtualHost *:<webapp-port>> ServerName <SomeServerName> WSGIScriptAlias / /home/<your-username>/webapps/<your-webapp-name>/apache2/conf/django.wsgi </VirtualHost>
  • Edit the django.wsgi file:

#!/usr/bin/python
import os, sys, site
# add virtualenv python libs
site.addsitedir('/home/<your-username>/webapps/<your-webapp-name>/ve/lib/python2.6/site-packages')
# append the project path to system path
sys.path.append('/home/<your-username>/webapps/<your-webapp-name>/ve/')
sys.path.append('/home/<your-username>/webapps/<your-webapp-name>/ve/<your-django-project-name>')
# set the settings module
os.environ['DJANGO_SETTINGS_MODULE'] = '<your-django-project-name>.settings'
# init the wsgi handler
from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()
  • Restart the Apache (/home/<your-username>/webapps/<your-webapp-name>/apache2/bin/restart) and everything should be fine!