In the previous installment of this series (in which we’re migrating this blog from WordPress to Django and PostgreSQL), we installed Apache, Python 2.6, psycopg and mod_wsgi on the server.

A note about the pace: I’m taking this very slowly, spelling out each and every setp. I realize that those of you with more Centos experience (which is probably pretty much everyone) or Django experience are probably tapping your feet and thinking, “Come on, get a move on here!” To those, I beg your indulgence. When reading about a topic I am unfamiliar with, I appreciate a high level of detail, so I don’t hit a weird error message only to be told, “Oh, right, you have to install an old version of x. I skipped that step.”

Installing Django

We have choices here: We can install Django directly from the current Subversion repository, or from an official release. We also have the choice of installing Django as local to a particular user, or global for the whole system. For this install, we’ll do an official release (1.1.1 is current), and install it for the whole system.

1[xof@blog ~]$ cd builds
2[xof@blog builds]$ wget http://www.djangoproject.com/download/1.1.1/tarball/
3[xof@blog builds]$ tar xvfz Django-1.1.1.tar.gz
4[xof@blog builds]$ cd Django-1.1.1
5[xof@blog Django-1.1.1]$ sudo python2.6 setup.py install
6[xof@blog Django-1.1.1]$ python2.6
7Python 2.6.3 (r263:75183, Oct 17 2009, 18:29:55)
8[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
9Type "help", "copyright", "credits" or "license" for more information.
10>>> import django
11>>>

Where is the project going to live?

Before we configure Apache and mod_wsgi to serve up our new Django project, we should decide where the project is going to live. I like to keep my builds and maintenance account separate from the user that contains the actual stuff being served up by the web server… so let’s create a new user just to hold the project:

1[xof@blog ~]$ sudo /usr/sbin/adduser thebuild
2[xof@blog ~]$ sudo passwd thebuild
3[xof@blog ~]$ sudo /usr/sbin/usermod -a -G apache thebuild

Note: Depending on the permissions your installation sets on /home directories by default, you may need to adjust them so that mod_wsgi can get at the files it needs.

Set up the Django project

Logging in as the project user, first, we’ll make sure that we get the right Python installation by adding an alias to our .bash_profile:

1alias python='/usr/bin/python2.6'

Just checking!

1[thebuild@blog ~]$ which python
2alias python='/usr/bin/python2.6'
3 /usr/bin/python2.6

And we can now create the project:

1[thebuild@blog ~]$ cd thebuild.com
2[thebuild@blog thebuild.com]$ python /opt/python2.6/lib/python2.6/site-packages/django/bin/django-admin.py startproject thebuild

We also need to create a directory for any Python eggs that our application might download (this is, by the way, a “feature” of Python that I really do not care for).

1[thebuild@blog thebuild.com]$ mkdir .python-eggs
2[thebuild@blog thebuild.com]$ chmod 770 .python-eggs

Now, we need to create a .wsgi file. The .wsgi file is the glue that connects the mod_wsgi module in Apache to our particular application. The Django project itself doesn’t (and shouldn’t) live under the webroot of the web server, but the .wsgi file does:

1[thebuild@blog thebuild.com]$ cd ~/thebuild.com
2[thebuild@blog ~]$ mkdir apache
3[thebuild@blog ~]$ cd apache
4[thebuild@blog apache]$ cat >>django.wsgi
5#!/opt/python2.6/bin/python
6
7import os, sys
8
9sys.path.append('/home/thebuild/thebuild.com')
10
11os.environ['DJANGO_SETTINGS_MODULE'] = 'thebuild.settings'
12os.environ['PYTHON_EGG_CACHE'] = '/home/thebuild/thebuild.com/.python-eggs'
13
14import django.core.handlers.wsgi
15
16application = django.core.handlers.wsgi.WSGIHandler()
17[thebuild@blog apache]$

Next, let’s create the media and admin-media directories, and move the admin media over from the source distribution:

1[thebuild@blog apache]$ mkdir admin-media
2[thebuild@blog apache]$ mkdir media
3[thebuild@blog apache]$ cd admin-media
4[thebuild@blog admin-media]$ cp -R /opt/python2.6/lib/python2.6/site-packages/django/contrib/admin/media/* .

Creating a User in PostgreSQL for the Blog

Right now, the only user in the PostgreSQL cluster is the default superuser; that’s not a great choice for the user that Django will use to connect to the database.

It’s also not idea to allow the Django user to create objects in the public schema, but it needs to be able to create objects in some schema if we’re going to use the cool Django model mechanism for table and field creation.

First, let’s create a database for the site:

1[postgres@blog ~]$ createdb thebuild
2[postgres@blog ~]$ psql thebuild
3psql (8.4.1)
4Type "help" for help.
5
6thebuild=# CREATE USER django ENCRYPTED PASSWORD 'password';
7CREATE ROLE

Then, let’s create a schema just to hold objects related to the site:

1thebuild=# CREATE SCHEMA thebuild AUTHORIZATION django;
2CREATE SCHEMA

And set an appropriate search path:

1thebuild=# ALTER ROLE django SET search_path TO thebuild, public;
2ALTER ROLE

… and revoke CREATE on the public schema from the user:

1thebuild=# REVOKE CREATE ON SCHEMA public FROM django;
2REVOKE

This means that, by default, anything that the django user creates will go into the schema thebuild, while it still has access to the schema public. However, it won’t be able to create new objects in public.

(Note that we still have rather a barn door open here: We haven’t done anything with pg_hba.conf to require passwords or other security to log in.)

Django Settings

We’ll need to update the settings.py file to match all of the various changes we made. So far, we’ve changed:

1DATABASE_ENGINE = 'postgresql_psycopg2'
2DATABASE_NAME = 'thebuild'
3DATABASE_USER = 'django'
4DATABASE_PASSWORD = 'password'
5
6TIME_ZONE = 'America/Los_Angeles'
7
8MEDIA_ROOT = '/home/thebuild/thebuild.com/apache/media/'
9MEDIA_URL = 'http://new.thebuild.com/media/'
10
11ADMIN_MEDIA_PREFIX = '/admin-media/'
12
13INSTALLED_APPS = (
14 'django.contrib.admin',
15 'django.contrib.auth',
16 'django.contrib.contenttypes',
17 'django.contrib.sessions',
18 'django.contrib.sites',
19)

Note that we’ve turned on the admin site by adding django.contrib.admin to installed apps. Now, we can do a syncdb to create the databases the installed apps require:

1thebuild@blog thebuild]$ python manage.py syncdb
2Creating table django_admin_log
3Creating table auth_permission
4Creating table auth_group
5Creating table auth_user
6Creating table auth_message
7Creating table django_content_type
8Creating table django_session
9Creating table django_site
10
11You just installed Django's auth system, which means you don't have any superusers defined.
12Would you like to create one now? (yes/no): yes
13Username (Leave blank to use 'thebuild'): admin
14E-mail address: xof@thebuild.com
15Password:
16Password (again):
17Superuser created successfully.
18Installing index for admin.LogEntry model
19Installing index for auth.Permission model
20Installing index for auth.Message model
21[thebuild@blog thebuild]$

Configure Apache

Apache can be tweaked endlessly, of course, but we’re going to do the minimum required to get the server up and ticking. Assuming we’ve done the basics (ServerName, etc.), we add the module info for mod_wsgi:

1LoadModule wsgi_module modules/mod_wsgi.so
2AddHandler wsgi-script .wsgi

Now, we need to point to the .wsgi file we created above. Moving over to /etc/httpd/conf.d, we can create a file thebuild.conf to contain the virtual host:

1<IfModule mod_alias.c>
2 Alias /media /home/thebuild/thebuild.com/apache/media
3 Alias /admin-media /home/thebuild/thebuild.com/apache/admin-media
4</IfModule>
5
6<IfModule mod_wsgi.c>
7 WSGIScriptAlias / /home/thebuild/thebuild.com/apache/django.wsgi
8 WSGIDaemonProcess thebuild processes=5 threads=6 display-name=%{GROUP}
9 WSGIProcessGroup thebuild
10 WSGIApplicationGroup %{GLOBAL}
11
12 WSGISocketPrefix run/wsgi
13</IfModule>

… restart Apache …

1[xof@blog conf]$ sudo /etc/init.d/httpd graceful

… and did it work?

it-lives

Exciting! Now, time to start actually designing our blog application.