Our Blogs Feeds:

"Doing with images makes symbols."

-Alan Kay

Blogs

Deploying Django on WebFaction

Published by Thomas Schreiber on April 9, 2009, 9:14 p.m.
Updated 20 February 2011

In the WebFaction control panel:

  1. Under Domains/Websites > Applications, create one application entry with app type of mod_wsgi 2.0/Python 3.x. (2.7 is used in this tutorial and is recommended for new projects, so replace accordingly if you need 2.6 or 2.5) I've named mine, projectname_python.
  2. Add two applications each with an app type of "Symbolic link to static-only app", naming one name media and the other static_collected. The extrainfo can be set to ~/projectname/src/projectname/static_collected and ~/projectname/src/media. Static site img, js, and css is kept in the PROJECT_ROOT/static/ folder and is under version control. However, all user generated content is kept outside of version control, but you can create a symlink to give the illusion the media folder is in the PROJECT_ROOT:
$ ln -s ~/projectname/src/projectname/media ~/projectname/src/media

I keep the user uploaded media in ~/projectname/media

  1. Under Domains/Websites > Domains, add the domain entries, and the www subdomains for each. The default
  2. Add the website entry, chose the three applications, and add all the subdomains.

Read the WebFaction docs for more details:

http://docs.webfaction.com/software/python.html

http://docs.webfaction.com/software/django/index.html

SSH into the WebFaction account and install git (find most recent version and replace x's)

$ wget http://www.kernel.org/pub/software/scm/git/git-1.7.x.x.tar.gz
$ tar zxf git-1.7.x.x.tar.gz
$ cd git-1.7.x.x
$ ./configure --prefix=$HOME
$ make
$ make install

Use SSH keygen to create a deployment key (needed to checkout the project from github)

$ mkdir ~/.ssh
$ cd ~/.ssh
$ ssh-keygen -t rsa  #simply press enter to skip through the options

(on github in the admin of the project, create a deployment key and copy the contents of id_rsa.pub to it)

Install Mercurial, virtualenv, and virtualenvwrapper and anything else that you want on the system Python path.

$ easy_install pip==dev
$ pip install Mercurial virtualenv virtualenvwrapper ipdb git+git://github.com/xvzf/vcprompt#egg=vcprompt

(add the following lines to the end of the ~/.bashrc)

export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python2.7
source /home/accountname/bin/virtualenvwrapper.sh

Create the virtualenv and git clone your project the project. To isolate the Python we tell the virtualenv not to inherit from system Python, and we tell it to use distribute.

$ mkdir ~/.virtualenvs/
$ source .bashrc
$ mkvirtualenv envname --no-site-packages --distribute
$ cd ~/projectname
$ mkdir src
$ cd src
$ git clone git@github.com:username/projectname.git

Now we need a settings_siteone.py and settings_sitetwo.py and also a siteone.wsgi and sitetwo.wsgi I prefer to keep all of these in the project root where the settings.py is by default.

The only difference between the two files is in settings_siteone.py SITE_ID = 1 and settings_sitetwo.py SITE_ID = 2. Here is what my settings_sitetwo.py looks like:

try:
    from settings import *
except ImportError:
    import sys
    sys.stderr.write("Unable to read settings.py\n")
    sys.exit(1)

SITE_ID = 2

We are using django-staticfiles and django-compressor to deal with collecting and compressing static media, both of which can be found on github. Django staticfiles is included in the contrib of Django >= 1.3, so the official documentation. Here are the pertinent parts of my settings.py to achieve.

# Absolute path to the root of the project.
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))

# URL of the project.  Leave out a network address to keep links relative.
# Make sure to use a trailing slash.
PROJECT_URL = '/'

# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/"
MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media/')

# URL that handles the media served from MEDIA_ROOT.
MEDIA_URL = '/media/'

# Absolute path to the directory that the mgmt cmd collectstatic dumps to.
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static_collected/')

# URL that handles the static files served from STATIC_ROOT.
STATIC_URL = '/static/'

# Additional directories which hold static files
STATICFILES_DIRS = [
    os.path.join(PROJECT_ROOT, 'static'),
]

ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/'

# URL that linked media will be read from and compressed media written to.
COMPRESS_URL = STATIC_URL

# Absolute file path that linked media will be read from and compressed media written to.
COMPRESS_ROOT = STATIC_ROOT

And at the bottom the main urls.py

from staticfiles.urls import staticfiles_urlpatterns
if settings.DEBUG:
    urlpatterns += patterns('',
        url(r'^media/(?P<path>.*)$', 'django.views.static.serve',
            {'document_root': settings.MEDIA_ROOT,
        }),
    )
urlpatterns += staticfiles_urlpatterns()

The .wsgi files should look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
import os, site, sys

# Tell wsgi to add the Python site-packages to it's path.
site.addsitedir('/home/accountname/.virtualenvs/envname/lib/python2.7/site-packages')

# Fix markdown.py (and potentially others) using stdout
sys.stdout = sys.stderr

# Calculate the path based on the location of the WSGI script.
project = os.path.dirname(__file__)
workspace = os.path.dirname(project)
sys.path.append(workspace)

os.environ['DJANGO_SETTINGS_MODULE'] = 'projectname.settings_siteone'
from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()

# Tell wsgi to add additional folders to it's Python path such as apps or lib.
from django.conf import settings
sys.path.insert(0, os.path.join(settings.PROJECT_ROOT, "apps"))
sys.path.insert(0, os.path.join(settings.PROJECT_ROOT, "lib"))

The only difference between the two files is telling wsgi which settings file to load:

os.environ['DJANGO_SETTINGS_MODULE'] = 'projectname.settings_sitetwo'

Lastly edit the apache config:

$ vim ~/webapps/projectname_python/apache2/conf/httpd.conf

Comment out or remove the DirectoryIndex DocumentRoot lines:

And add (where xxxxxx is the WebFaction assigned port number for the wsgi application):

NameVirtualHost *:xxxxxx

<VirtualHost *:xxxxxx>
    ServerName domain1.com
    ServerAlias www.domain1.com
    WSGIScriptAlias / /home/accountname/projectname/src/projectname/siteone.wsgi
</VirtualHost>

<VirtualHost *:xxxxxx>
    ServerName domain2.com
    ServerAlias www.domain2.com
    WSGIScriptAlias / /home/accountname/projectname/src/projectname/projectname/sitetwo.wsgi
</VirtualHost>

Requirements are kept under version control in a file named requirements.txt. Here are the basics, psycopg2 is the PostgreSQL db adapter.

django>=1.2, <1.3
south==0.7.3
django-staticfiles==1.0b1
git+git://github.com/jezdez/django_compressor@0.6b2#egg=django-compressor-0.6b2
psycopg2

After activating the virtualenv and installing some necessary and helpful modules, run the requirements file with pip.

$ ~/.virtualenvs/envname/bin/activate
$ easy_install pip==dev
$ pip install Mercurial virtualenv virtualenvwrapper ipdb 
$ pip install -r requirements.txt

That should be it, sync the database, collect the staticfiles, restart apache and check if it works.

$ ~/projectname/src/projectname/.manage.py syncdb
$ ~/projectname/src/projectname/.manage.py collectstatic
$ ~/webapps/myproject/apache2/bin/stop
$ ~/webapps/myproject/apache2/bin/start

If you want to send emails, add the following to your settings.py:

RECIPIENT_LIST = ['name@yourdomain.com',]
DEFAULT_FROM_EMAIL = "name@yourdomain.com"
SERVER_EMAIL = "name@yourdomain.com"

EMAIL_HOST = 'smtp.webfaction.com'
EMAIL_HOST_USER = 'mailbox_username'
EMAIL_HOST_PASSWORD = 'mailbox_password'
EMAIL_PORT = 25

Not working? First review step one (control panel for accuracy) then check the logs.

$ cat ~/logs/user/error_projectname_python.log

http://docs.webfaction.com/software/general.html#why-do-i-get-the-site-not-configured-error-message

blog comments powered by Disqus