Updated 20 February 2011
In the WebFaction control panel:
- 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.
- 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
- Under Domains/Websites > Domains, add the domain entries, and the www subdomains for each. The default
- 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