Old School Django

2015-08-15

I recently gave a talk at MelbDjango 2.3 in which I covered recent news in the Django world, including Django’s 10th birthday as an open source project. Everyone loves a demo, so I set out to get a 10 year old version of Django running in a modern environment.

Warning: This guide covers how to get an ancient version of django running for demo/nostalgia purposes only. Don’t use the instructions here for a production server! I should probably also point out that I am quite new to Django in general.

Prerequisites

Installation

Preparation

Create a new folder where you will put your old school Django project. I called mine demo.

Create a virtualenv and activate it.

Old School Django

Clone the Django git repo and checkout commit b68c478aa5d890e76aae6e2f695220505618c8e0, the version that we will be using. I’ve cheated slightly here - we’ll be using a Django 6 days newer than the first public commit since that’s when they added the runserver command. Prior to this you would need to use mod_python on Apache, which I have no experience with.

$ git clone https://github.com/django/django django
Cloning into 'django'...
remote: Counting objects: 326439, done.
remote: Compressing objects: 100% (104/104), done.
remote: Total 326439 (delta 39), reused 0 (delta 0), pack-reused 326335
Receiving objects: 100% (326439/326439), 139.61 MiB | 13.36 MiB/s, done.
Resolving deltas: 100% (230621/230621), done.
Checking connectivity... done.

$ cd django

$ git checkout b68c478aa5d890e76aae6e2f695220505618c8e0
Note: checking out 'b68c478aa5d890e76aae6e2f695220505618c8e0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

  HEAD is now at b68c478... Added 'django-admin.py runserver', which starts a lightweight development server running Django on a local port

Modify the database wrapper to use the psycopg1 compatibility layer from psycopg2. This version of Django needs psycopg1. For my demo at the meetup, I built and installed psycopg1 which required some careful tweaks to the ./configure command as well as a later makefile edit so all the postgres include files could be found. I stumbled across a better way while writing this post. We’ll modify the database wrapper to use the psycopg1 compatibility mode of psycopg2. Edit the django/core/db/backends/postgresql.py file.

"""
PostgreSQL database backend for Django.

Requires psycopg 1: http://initd.org/projects/psycopg1
"""

from django.core.db import base, typecasts

# import psycopg as Database  # comment out this line

# insert these lines
from psycopg2 import psycopg1 as Database
Database.register_type = Database.extensions.register_type
Database.new_type = Database.extensions.new_type

...

Install Django to your virtualenv from the repo root directory.

(env)$ pip install .
Unpacking /path/to/django
  Running setup.py (path:/tmp/pip-q0veYw-build/setup.py) egg_info for package from file:///path/to/django
      
  Installing collected packages: django
    Running setup.py install for django
        changing mode of build/scripts-2.7/django-admin.py from 664 to 775
            
        changing mode of /path/to/project/env/bin/django-admin.py to 775
Successfully installed django
Cleaning up...

Other python packages

Install psycopg2.

(env)$ pip install psycopg2

Setup your old school django project

Create the new project

Create a new project using the django-admin.py file and inspect the generated structure. Not too different from the modern django structure.

(env)$ django-admin.py startproject demo
.
├── apps
│   └── __init__.py
├── __init__.py
└── settings
    ├── admin.py
    ├── __init__.py
    ├── main.py
    └── urls
        ├── admin.py
        ├── __init__.py
        └── main.py

Project config

Edit the settings/main.py file.

Add your project to your python path. Create a file called demo.pth in the site-packages dir of your virtualenv (eg env/lib/python2.7/site-packages/demo.pth). The file should contain the absolute path of the folder containing your django project. My project folder is /Users/karl/dev/demo/demo so I added /Users/karl/demo to the demo.pth file.

Create an environment variable called DJANGO_SETTINGS_MODULE that specifies your settings module.

(env)$ export DJANGO_SETTINGS_MODULE=demo.settings.main

Edit your settings/main.py file and add your database settings. I found that leaving DATABASE_HOST blank didn’t work (despite the comment claiming otherwise) and I had to specify localhost explicitly. Now is probably also a good time to make sure this database exists!

# main.py file

...

DATABASE_ENGINE = 'postgresql' # 'postgresql' or 'mysql'
DATABASE_NAME = 'demo'
DATABASE_USER = 'demo'
DATABASE_PASSWORD = 'demo'
DATABASE_HOST = 'localhost'             # Set to empty string for localhost

...

Now try setting up the database and running the inbuilt server.

(env)$ django-admin.py init
(env)$ django-admin.py runserver

Navigate to http://localhost:8000. You should see a 404 error. Congratulations, Django is up and running!

Write a demo app to prove that we actually have Django working

Create a new app

Navigate to the apps folder in your Django project. Create a new app called counters. This will be a super-simple page hit counter to prove that we are serving requests and hitting the database.

(env)$ django-admin.py startapp counters

The apps folder should now look like:

.
├── counters
│   ├── __init__.py
│   ├── models
│   │   ├── counters.py
│   │   └── __init__.py
│   ├── urls
│   │   ├── counters.py
│   │   └── __init__.py
│   └── views
│       └── __init__.py
└── __init__.py

Create a model

Edit the counters/models/counters.py file:

from django.core import meta

# Create your models here.

class Counter(meta.Model):
    fields = (
        meta.IntegerField('count', 'count', default=0),
    )

Install the app

Add the app to your INSTALLED_APPS in your settings/main.py file:

...

INSTALLED_APPS = (
    'demo.apps.counters',
)

...

Install the app.

(env)$ django-admin.py install counters

Add a template and view

Now add a super simple template in a suitable templates folder (and add that folder to your TEMPLATE_DIRS variable in your settings file).

<html>
<head>
    <title>Ancient Django</title>
</head>
<body>
    <h1>{% raw %}{{ count }}{% endraw %} hits</h1>
</body>
</html>

Edit your apps/counters/views/views.py file:

from django.core import template_loader
from django.core.extensions import DjangoContext as Context
from django.utils.httpwrappers import HttpResponse

from demo.apps.counters.models.counters import counters


def index(request):
    """
    Displays how many times the page has been accessed by accessing
    and incrementing a Counter model. A Counter instance is created if
    no counters exist.
    """
    counters_list = counters.get_list()
    counter = None
    if len(counters_list) == 0:
        counter = counters.Counter(id=None)
        counter.count = 0
    else:
        counter = counters_list[0]

    counter.count += 1
    counter.save()

    t = template_loader.get_template('counters/counter')
    c = Context(request, {
        'count': counter.count,
    })
    return HttpResponse(t.render(c))

Add some URLs

Finally, we need some urls. Edit the urls/main.py file and add an entry pointing everything to our counters app.

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^', include('demo.apps.counters.urls.counters'),
)

Then edit the apps/counters/urls/counters.py file and point everything to the index view.

from django.conf.urls.defaults import *


urlpatterns = patterns(
    'demo.apps.counters.views',
    (r'^', 'index'),
)

Run django-admin.py runserver again and you should see your hit counter increment with each page load!

More

There’s a docs folder inside the Django repo. The files are somewhat disorganised, but between them and the source code you should be able to figure out whatever it is you want to do. You can always cheat by jumping ahead into the future to view the next few parts of the tutorial.

You can get the admin running by pointing DJANGO_SETTINGS_MODULE at the demo.settings.admin module. You’ll need to create a superuser via a python shell first. Check out docs/tutorial02.txt for details. Unlike modern Django, it seems as though the admin was never intended to run at the same time as your main site.

Reflections

10 years is a long time in the software world, but it was quite refreshing how easy it was to get a 10 year old version of Django running. Many things are still very similar, such as the project/app structure, the polls tutorial, the admin theme and comments like “# Create your models here” in the auto-generated model files. For a project that is now as large and polished as Django is, it was also nice to see some incomplete documentation with lines such as “The tutorial ends here for the time being. But check back within 48 hours for the next installments”. I could almost feel the excitement of the developers as they unleashed their previously in-house creation on the world.

Happy 10th birthday, Django. Hopefully many more to come.