Working with the Django admin and legacy databases, pt.2

Django    2009-04-26

By this time, I'm several weeks into the project and it's time to get you all caught up - let's see if I can break this down into digestable bites.

(Btw, don't expect it to you take this long - this isn't an officially sanctioned work project for me, so I'm devoting some of my free time to it in hopes of selling the company on adopting Django eventually. It's taking me weeks because I am notoriously selfish about my free time.)

As I mentioned in part 1, I'm running this on mod_python in a dev environment. Installing/configuring mod_python is outside the scope of this post, but there's some helpful information in the official Django documentation:

http://docs.djangoproject.com/en/dev/howto/deployment/modpython/

If it helps, here's a sample based on my conf:

ServerRoot "/etc/apache2"

LoadModule dir_module modules/mod_dir.so
LoadModule env_module modules/mod_env.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule python_module modules/mod_python.so
LoadModule rewrite_module modules/mod_rewrite.so

KeepAlive On
Listen 80
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog logs/access_log combined
ServerLimit 2

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        ServerName myadmindemo.MYHOST.com
        ServerAlias myadmindemo.MYHOST
        ErrorLog /var/log/apache2/myadmindemo.error.log
        LogLevel warn
        CustomLog /var/log/apache2/myadmindemo.access.log combined
        DocumentRoot /web04/myadmindemo
Alias /media/ /web04/library/Django-1.0.2-final/django/contrib/admin/media/
<Location "/">
    PythonHandler django.core.handlers.modpython
    PythonPath "['/web04/library/Django-1.0.2-final/django', '/web04/library/lib','/web04/myadmindemo','/web04'] + sys.path"
    SetEnv DJANGO_SETTINGS_MODULE myadmindemo.settings
    SetEnv DJANGO_LOG_DIR '/web04/logs/'   ## a setting I use for custom logging - more on that later
    SetHandler python-program
    PythonDebug On
</Location>
<Location "/media">
    SetHandler none
</Location>
</VirtualHost>

I'm also assuming you have Django installed already. If not, your next stop should be:

http://docs.djangoproject.com/en/1.0/topics/install/

Django's legacy database documentation also covers most of these basics - you can find that here:

http://docs.djangoproject.com/en/dev/howto/legacy-databases/

Go ahead and create your Django project (in my sample, I've already included '/web04/myadmindemo' in my pythonpath, so I'd be running this in '/web04'):

    django-admin.py startproject myadmindemo

Start by making the appropriate changes to your settings file:

    vi myadmindemo/settings.py
  1. Configure your project to point to the existing db. (This probably goes without saying, but you probably shouldn't build against your real db. Clone it to a test db and transfer enough sample data into it to be a fair representation.)
  2. Make sure you have 'django.contrib.admin' in your INSTALLED_APPS.

Next, run the script that crawls the database and generates model code based on that introspection:

    python myadmindemo/manage.py inspectdb > myadmindemo/models.py

Finally, run your sync - this walks you through setting up your admin user and creates Django's administrative tables (auth_* and django_*). (Please be sure to run this as a last step. An embarrassing admission: on my first run, I was on autopilot - I ran syncdb before inspectdb and wound up introspecting the django and auth tables along with everything else):

    python myadmindemo/manage.py syncdb

There's one more standard installation bit you'll need to tweak - be sure to enable the admin in the urls.py in your project root.

Assuming you've gotten this far without errors, you might want to restart apache. Now go take a look at your shiny new admin - you won't see any data there yet, but you should at least be able to log in. (If you're working locally, on OS X for example, just pop open a browser and go to http://127.0.0.1:8000/. Otherwise, look at value you set for your ServerAlias.)

Now go take a look at the models.py you generated with inspectdb. It's a mess isn't it? That's how you know that the real work is about to begin.

Admin.py and urls.py

Here's another embarrassing admission: Again, I was on autopilot when I set up my project, and I copied an admin.py and urls.py over from another project that was running on an older version of Django. I was able to log in on my first try, but there were a few places where I got this mystery error:

    invalid literal for INT() with base 10

Through my old-fangled admin.py, I was re-registering the User and Group classes. Among other things, that kept the change password form from working (there's a little more detail here).

I reworked my urls.py like so:

	from django.conf.urls.defaults import *

	from django.contrib import admin
	admin.autodiscover()

	urlpatterns = patterns('',
	    (r'^admin/(.*)', admin.site.root),
	    (r'^(.*)$', admin.site.root),
	)

I also moved the model/admin class registry out of the single admin.py that I generally keep at the project root, into smaller admin.py files within each app.

	from myadmindemo.content.models import MyContent

	from django.contrib import admin

	admin.site.register(MyContent)

Splitting models into app folders

In my case, I'm working with a database with nearly a hundred tables (how important some of those tables are has long been up for debate, but it is what it is). Luckily, there is a clear division between the topics of these tables' content - news, events, products, among others. Although I am only planning to use the Django admin, not create a bunch of new applications, I went ahead and split the models into app folders anyway. I'm not sure that James Bennett (with two T's!) would agree with my decision, but in the long run it's going to make the model classes easier for me to manage.

If you go this route, take all the model classes from:

    myadmindemo/models.py

And split them up thusly:

    myadmindemo/
	event/
	    __init__.py
	    admin.py
	    models.py
	news/
	    __init__.py
	    admin.py
	    models.py
	product/
	    __init__.py
	    admin.py
	    models.py

Add each app to INSTALLED_APPS in settings.py:

	'myadmindemo.news',
	'myadmindemo.product',
	'myadmindemo.event',

Next up, the stuff I know you're actually looking for: model cleanup.