Deploying Django

Deploying and scaling live dynamic websites is a topic easily big enough to fill an entire semester or more; we're just going to barely scratch the surface.

The database

We've been using the SQLite database, which is great for development. It keeps all its data in a single file, right there in your project directory, so you don't have to worry about database users, permissions, configuring a database server, etc.

So why not just deploy with SQLite? SQLite doesn't support concurrency. In other words, you get more than one person using your website at the same time, and one of them is likely to get "database locked" errors. Not good.

Picking a database server

We need something more robust than SQLite. Commercial options include Oracle or Microsoft SQL Server, but we don't have tens (or hundreds) of thousands of dollars handy at the moment.

Fortunately, there are free open-source alternatives that are more than capable of handling the needs of a dynamic web site. The two most popular open-source options are MySQL and PostgreSQL. I prefer Postgres because it is stricter about ensuring data integrity. MySQL has a reputation for being easier to administer. Either would be a fine choice for most websites. For today we'll use Postgres, but from the Django side using MySQL wouldn't be much different.

Creating your database

Since this isn't a class on database server administration, I've gone ahead and set up Postgres on dj.goshen.edu, and created an account for each of you, with the same username as your system shell user (and your password same as the username - don't do this normally). From the dj.goshen.edu shell prompt, use the Postgres utility program createdb to create yourself a database: createdb yourusername (to keep things simple, we're making your database name the same as your username also).

Changing your DB settings

In your settings.py file, you have a set of DATABASE_* settings telling Django what database to use. We'll need to change these for your live site, to use Postgres instead of SQLite. But you'll still want to use SQLite for development.

How can we have different database settings on two different clones of the same code? One possibility is to just change them in the "live" clone, and never commit or pull from that clone. I use a more general solution: I move the "default" settings into a file called global_settings.py, and then have a very simple settings.py file that imports all the settings from global_settings and overrides whichever ones it needs to for that particular deployment. I put this simple settings.py in .hgignore so it never gets committed in Mercurial (thus it can be different for each clone).

Making global_settings happen

In the directory where your settings.py file lives, run: hg mv settings.py global_settings.py Commit this, then create a new file settings.py with the following single line:

from global_settings import *

Add this settings.py file to your .hgignore (so it doesn't show up in hg status), and commit that change as well. For now this is all we need; everything will work the same as before, except you'll need to create this short settings.py wherever you run this code.

Create a "live" checkout

From a dj.goshen.edu shell, run the following commands to set yourself up a www/ directory in your homedir. This is where we'll put all your live web code.

$ deactivate
$ cd
$ mkdir www
$ cd www

Once you're inside your ~/www/ directory, use hg clone to get a fresh clone of your codebase. You don't want to be developing on the same code that's running your live site! Run virtualenv yourproject_env to get yourself a fresh virtualenv for the live site, activate the new env, and then pip install -r requirements.txt to get all your dependencies (you have been keeping that up to date when you install new things, right?). (You don't actually have to easy_install pip anymore; the new version of virtualenv automatically includes pip; but it doesn't hurt if you do.)

A place for the database settings

Now create a settings.py in your live clone that looks something like this:

from global_settings import *

DEBUG = False
TEMPLATE_DEBUG = False
DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME = 'yourusername'
DATABASE_USER = 'yourusername'
DATABASE_PASSWORD = 'yourusername'
DATABASE_HOST = 'localhost'

This imports all the default settings, then overrides the database settings so it will use your Postgres database (and to turn DEBUG off; a live deployment with DEBUG on will use up memory over time). You should now be able to run python manage.py syncdb (and migrate if you're using South) in this live clone without errors.

Hooking up Apache

In development, we've been using python manage.py runserver as our web server. Great for development, even auto-reloads when your files change. Just like SQLite, it's not good for a real deployment: it also only supports a single user at a time, and is relatively slow to boot. Instead, we'll use by far the most popular webserver on the internet: Apache.

apache.conf

To hook up your project to Apache, you'll need two new files: an Apache config file, and a WSGI script file. Create the Apache config file at ~/www/apache.conf; it can look like this for now:

<VirtualHost *:80>
    ServerName yourusername.dj.goshen.edu
    WSGIScriptAlias / /home/yourusername/www/yourprojectclone/deploy.wsgi
</VirtualHost>

Obviously replace yourusername and yourprojectclone with the appropriate names.

deploy.wsgi

WSGI is the Web Standard Gateway Interface, a Python standard that dictates how web servers communicate with Python web application code. Your WSGI script file is a sort of "entry point" into your application. For now, just copy mine from my feedback project and place it in your project at the top level next to requirements.txt. The only things in it you'll need to change are the name of your virtualenv in the fourth line, and near the bottom where it sets DJANGO_SETTINGS_MODULE to 'feedback.settings', replace 'feedback' with the name of your project directory (the directory containing settings.py).

Serving media

If you look in your urls.py, you'll see the section that serves your media files is wrapped in if settings.DEBUG:. DEBUG is now False, so Django won't serve your media. There's good reason for this; when you have a fast webserver like Apache available, tuned to serve up static files super-quickly, it's very inefficient to go through Django for all your media files; better to just have Apache serve them directly. So let's set that up by adding this to your apache.conf file:

<VirtualHost *:80>
    ServerName yourusername-media.dj.goshen.edu
    DocumentRoot /home/yourusername/www/yourprojectclone/yourproject/media/
    Alias /admin/ /home/yourusername/www/yourprojectclone/your_env/lib/python2.6/site-packages/django/contrib/admin/media/
</VirtualHost>

Telling Django where to find your media

We also need to tell Django that your media is available at http://yourusername-media.dj.goshen.edu/ rather than /media/. Just add this to your new settings.py file along with the database settings:

MEDIA_URL = "http://yourusername-media.dj.goshen.edu/"
ADMIN_MEDIA_PREFIX = MEDIA_URL + "admin/"

Fire it up!

At this point, someone with administrator rights on dj.goshen.edu (either Paul or I) needs to hook up your apache.conf file into apache, and tell it to reload its configuration files. Once that's done, you should be able to visit http://yourusername.dj.goshen.edu and see your site.

Telling Apache to reload your code

Unlike runserver, Apache doesn't automatically reload your code anytime the files change. If you commit some new features and pull them into your live clone and update, you still won't see them on the live site. To get Apache to reload your code, you just touch the WSGI script file: touch deploy.wsgi, and it'll reload.

Moving beyond dj.goshen.edu

If you want to deploy your site for the real world to use, you won't be able to use dj.goshen.edu (it might go away sometime after this class ends?). You'll have to pay for real web hosting. For a small site, my top recommendation is Webfaction.com: they have shared hosting plans from $5/mo and up, and they explicitly support Python and Django (many cheap web hosts only allow PHP).

If your site gets popular, you might need to move beyond shared hosting and get your own VPS (Virtual Private Server -- that's what dj.goshen.edu is). They typically cost $20/mo and up. With a VPS, you get better performance and more system resources than with shared hosting, but you have to do all the system administration yourself. I use (and am happy with) Linode; other popular providers include Slicehost and Rackspace Cloud.