Django Setup

Introduction

I'm relatively new to Django, and maybe to servers in general, but setting up Django is a chore. Or at least figuring out how to set it up is. There is no installer. You have to have a pretty good understanding of how your HTTP server (IIS, Apache, whatever) works before you're going to be able to get anywhere. Then you'll have to know how to set up quite a few ancillary applications (e.g. Python, WSGI, FastCGI, Flup, MySQL, PostgreSQL). The details of which other applications you want to set up depend entirely on the evironment you're building - so that means it is a little different every time - and that means it is pretty hard to write one single "walkthrough" for how you should do it.

What I want to do here is detail my setup, and along the way make some suggestions about what seems to make things easier. So here goes:

Suggestion: Use Ubuntu (Most recent stable version, always.)

I say this because that apt system really makes installations a breeze. I fumbled for a while to try and setup WSGI (a module for Apache) to work on my Windows machine, to no avail. In Ubuntu it is so easy:

sudo apt-get install apache2 libapache2-mod-wsgi

... and you're good to go. Really, when you're dealing with FOSS (free and open-source software), Ubuntu is just really, really nice. And if you're interested in setting up Django you should get used to command-line anyways. 

Suggestion: Write down what you do. All of it.

I say this because it makes it easier for you to recreate your development server environment on a real-life production system. And yes, you definitely want to separate the two.

Suggestion: Use Virtual Machines for development

Virtual machines are just the only way to go for server development. You can take snapshots, rollback changes you don't like, and never have to worry about something you do messing up your desktop environment. You also don't have to worry much about security, as your VM can be set up to only be accessible by the computer hosting the VM. I use VirtualBox, because it is free, and because it is easy to use.

Get on with it: My Setup

Here are the basics of my installation, and why I use the components that I use:

  1. Ubuntu 10.10 Server on VirtualBox: Resons detailed above.
  2. Apache HTTP Server: This is the HTTP Server I'm the most familiar with, and it is wildly popular, so it must be pretty good.
  3. Apache module WSGI: Allows for interactions between Python and Apache.
  4. PostgreSQL and PostGIS: I am a cartographer at heart, so I always use a spatially-enabled database. GeoDjango is really cool, and works nicely with PostGIS.
  5. Samba: Makes it easy for me to edit files on the Ubuntu VM using Windows programs that I'm familiar with (i.e. Notepad++).
  6. Git: It is smart to manage your Django apps with some kind of version control, and Git is nice. So is GitHub.
  7. Subversion: More version control, and everyone uses it, so it must be pretty good.

This walkthrough then is going to assume that you're starting from a completely fresh Ubuntu installation. Adjust as neccessary if you're not.

  1. Always stay up-to-date
    sudo apt-get update
    sudo apt-get upgrade
  2. Install the Sun JDK: OpenJDK seems to work most of the time, which really isn't good enough. Use the Sun JDK.
    sudo add-apt-repository ppa:sun-java-community-team/sun-java6
        (if that doesn't work, you may not have the add-apt-repository application installed.
            sudo apt-get install python-software-properties
        and then try the first line again.)
    sudo apt-get update
    sudo apt-get install sun-java6-jdk
  3. Now, install everything else:
    sudo apt-get install apache2 postgresql-8.4-postgis samba subversion libapache2-mod-wsgi python-psycopg2 git
  4. The first thing I like to configure is Samba, so that I can adjust configuration files using Windows software:
    1. Set the root user's password, allow that user to connect to Samba:
      sudo passwd root {your secret password}
      sudo smbpasswd -a root {your secret password}
      sudo nano /etc/samba/smb.conf
    2. This will open up Nano, and you'll be editing the Samba configuration file. Add the following lines to the bottom of the file:
      [Root]
      path = /
      writeable = yes
    3. Now restart the Samba service to accept your changes:
      sudo service smbd restart
      sudo service nmbd restart
  5. Suggestion: Don't do this on your production server. You've just allowed root access to your Ubuntu VM's file system. This is a gaping security hole.

  6. Next, configure PostgreSQL to listen to the right places.
    1. Configure the postgres user. This is the PostgreSQL superuser:
      sudo passwd postgres {a secret password}
      su postgres (you'll be prompted for your secret password)
      psql -c "ALTER user postgres WITH PASSWORD '{a secret password}'"
      exit
    2. Now make the following adjustments to the file located at /etc/postgresql/8.4/main/pg_hba.conf
      # near the bottom of the file, change:
          local   all         all                               ident
      # to:
          local   all         all                               password
      
      # Add the following line:
          host	all	all	192.168.1.0/24	trust
      ...making sure to change the IP range to that of your LAN
    3. Make the following adjustments to the file at /etc/postgresql/8.4/main/postgresql.conf
      # Change the line to read:
          listen_addresses = '*'
    4. Restart PostgreSQL to accept your changes:
      sudo /etc/init.d/postgresql restart
  7. Setup a PostGIS database for Django to use:
    1. Create a PostGIS template database:
      su postgres (you will be prompted for your password)
      createdb -E UTF8 template_postgis
      createlang -d template_postgis plpgsql
      psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';"
      psql -d template_postgis -f /usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql
      psql -d template_postgis -f /usr/share/postgresql/8.4/contrib/postgis-1.5/spatial_ref_sys.sql
      psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;"
      psql -d template_postgis -c "GRANT ALL ON geography_columns TO PUBLIC;"
      psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
    2. Create the spatial database for Django to use from that template:
      createdb -T template_postgis django
      This creates a database called "django". Adjust as you see fit.
    3. Create a user that Django will use to manage the database. You need to create a UNIX user and a PostgreSQL one:
      exit (if you are still logged in as UNIX user "postgres")
      sudo adduser mister_django (you will be prompted to give the user a password)
      su postgres (you will be prompted for the password)
      psql -c "CREATE USER mister_django WITH PASSWORD '{the secret password}'"
      psql -c "GRANT ALL PRIVILEGES ON DATABASE django TO mister_django"
  8. Install Django
    1. "Installing" Django really just means putting the code for it somewhere. You get the code from the Django SVN repository: Put it in a location that will not be accessible by your HTTP Server. I usually do it in /home/{my user name}/django-trunk.
      cd ~
      svn co http://code.djangoproject.com/svn/django/trunk/ django-trunk
    2. You need to create a couple of symlinks that will make the django code accessible to Python, and to you when you need to call it:
      ln -s ~/django-trunk/django /usr/lib/python2.6/dist-packages
      ln -s ~/django-trunk/django/bin/django-admin.py /usr/local/bin
  9. Start and your Django Project

    Suggestion: Understand the differences between a Django Project and a Django App.

    A Django Project represents essentially one "site". It manages your database configuration and is configured for the HTTP server of your choice. It knows its way around your file system enough to know where you keep css, javascipt, images and other "static" media files. It is specifically tailored to your particular server environment, and to that end it is good to think of it as "Your Django Server". You only need one. You don't need to share it with other people or computers.

    A Django App, on the other hand, is a component of a Django Project that does something. If written well, it should be able to be plugged into anyone's Django Project, anywhere, without issue. A Project can contain any number of Apps. I use Git to manage the files that make up my Django Apps, but I don't bother with files that make up my project. This is because the project configuration will have to be different on every computer I put it on. I would rather just deal with source control in situations that can be distributed and installed more or less as-is.

    1. Build a Django Project by calling django-admin.py. You can put this anywhere you like, but I find it a useful convention to put it in /home/{my user name}/django-projects/{project's name}:
      cd ~
      mkdir django-projects
      cd django-projects
      django-admin.py startproject {your project's name}
    2. Setup directories in which to place your static media. If the term "Static Media" seems a bit vague, it is. Essentially it means files that will be served directly, or "as-is", by the HTTP Server. This is as opposed to what you might think of as "Dynamic Media", or files which are created by Django on-the-fly when requested by a client. These "Dynamic Media" do not persist as files on your server itself.
      cd ~/django-projects/{your project's name}
      mkdir media
      mkdir static
      
    3. Make a symlink to the static media for Django's admin site. These files came with the code that you pulled from SVN:
      sudo ln -s ~/django-trunk/django/contrib/admin/media ~/django-projects/{your project's name}/admin_media
  10. Get Apache to serve your Django Project
    1. Make a location for the file that Apache will serve, which essentially calls your project itself. This should not be in the same location as your project. I usually put it in /home/{my user name}/django-sites/{site name}
      cd ~
      mkdir django-sites
      cd django-sites
      mkdir {your site's name}
    2. Inside that directory, create a file called {your project's name}.wsgi. The file should look like this:
      import os
      import sys
      
      os.environ['DJANGO_SETTINGS_MODULE'] = '{your project's name}.settings'
      
      path = 'home/user/django-projects/'
      if path not in sys.path:
          sys.path.append(path)
      another_path = '/home/user/django-projects/{your project's name}'
      if another_path not in sys.path:
      	 sys.path.append(another_path)
      
      import django.core.handlers.wsgi
      application = django.core.handlers.wsgi.WSGIHandler()
      This file sets a Python environment variable that contains your site's settings. Then it makes sure that the location of the code you've written for your project is accessible to Python by putting the locations in your PYTHONPATH. Finally, it calls a core Django function to run your site.
    3. Configure Apache to serve that file. You'll do this by making a VirtualHost file. My example here sets up a VirtualHost that responds to any traffic on port 80. You can adjust this as needed if you'd like your site to show up as a subdomain, or some folder on your existing domain. Basically you need to tell Apache to serve the static, media and admin media files, as well as the .wsgi file that you created that represents your site's "dynamic media". This file should be placed in /etc/apache2/sites-available and the file should be symlinked to /etc/apache2/sites-enabled. If there are any pre-existing files in the sites-enabled directory, you'll need to make sure that they select different traffic than your site, or else remove the pre-existing files. Here is an example of a VirtualHost file for a Django site:
      <VirtualHost *:80>
      	Alias /media/ /home/{your user's name}/django-projects/{your project's name}/media/
      	<Directory /home/{your user's name}/django-projects/{your project's name}/media/ >
      Order deny,allow Allow from all </Directory> Alias /static/ /home/{your user's name}/django-projects/{your project's name}/static/
      <Directory /home/{your user's name}/django-projects/{your project's name}/static/ >
      Order deny,allow Allow from all </Directory> Alias /admin_media/ /home/{your user's name}/django-projects/{your project's name}/admin_media/
      <Directory /home/{your user's name}/django-projects/{your project's name}/admin_media/ >
      Order deny,allow Allow from all </Directory> WSGIScriptAlias / /home/{your user's name}/django-sites/{your site's name}/django.wsgi
      <Directory /home/{your user's name}/django-sites/{your site's name}/ >
      Order allow,deny Allow from all </Directory> </VirtualHost>
    4. Restart Apache:
      sudo /etc/init.d/apache2 restart
  11. Configure your Django Project: settings.py. In the folder representing your Django Project, you'll find a file called settings.py. This is the main configuration file for your project. Make the following adjustments to it:
    1. Setup your database connection:
      DATABASES = {
          'default': {
              'ENGINE': 'django.contrib.gis.db.backends.postgis', 
              'NAME': '{your database name - mine was "django"}',                      
              'USER': '{your database user - mine was "mister_django"}',                      
              'PASSWORD': '{your secret password}',                 
              'HOST': '',                      
              'PORT': '',                      
          }
      }
    2. Tell Django where you'll put static media:
      MEDIA_ROOT = '/home/djangoadmin/django-projects/{your project's name}/media/'
      MEDIA_URL = '/media/' (this is the alias in your VirtualHost file above)
      STATIC_ROOT = '/home/djangoadmin/django-projects/{your project's name}/static/'
      STATIC_URL = '/static/' (again, alias in VirtualHost file)
      ADMIN_MEDIA_PREFIX = '/admin_media/' (again, alias in VirtualHost file)
    3. Tell Django where to look for URL resolution rules:
      ROOT_URLCONF = '{your project's name}.urls'
    4. Adjust the INSTALLED_APPS list. You must include the GIS app, and I strongly encourage you to use the admin app.
      INSTALLED_APPS = (
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.sites',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'django.contrib.admin',
          'django.contrib.gis',
      )
  12. Adjust URL recognition - enable the Admin interface

    Adjust your URLconf file, urls.py, located in your project directory. Make it look like this:

    from django.conf.urls.defaults import *
    
    from django.contrib import admin
    admin.autodiscover()
    
    urlpatterns = patterns('',
        (r'^admin/', include(admin.site.urls)),
    )

    This makes the admin interface accessible at http://{your host name}/admin/

  13. Sync your database. This is an easy one:
    cd ~/django-projects/{your project's name}
    python manage.py syncdb

    You'll be prompted to create a superuser, which you should do. That's who you'll use to log into the admin interface.

At this point you're ready to start writing code. Note that whenever you change any python code, you won't see the changes until you restart Apache:

sudo /etc/init.d/apache2 restart

It seems to me there must be a better way, but so far that's all I've been able to come up with.