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.
Create a new folder where you will put your old school Django project. I called mine demo
.
Create a virtualenv and activate it.
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...
Install psycopg2.
(env)$ pip install psycopg2
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
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!
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
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),
)
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
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))
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!
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.
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.