I recently came across one feature of Django that seemed pretty useful for one
off projects and customizations and was startled because it's one of Django's
least mentioned features. In fact, I've been using Django at work for over 5
years now and didn't hear of it until this last week.

This feature is the manage.py command inspectdb which inspects the
tables in an existing database and creates Python code defining the Django
models for those tables. We had a use case at work where we had a database
table that was not managed as a Django model, but we wanted to create tests
interacting with that table. The solution was to use inspectdb to create a
models.py file in a test application and add that application to
INSTALLED_APPS when running tests. That way the table is created via syncdb
when the tests are run, and we can use the model to create/check test data.

One of my other co-workers mentioned that inspectdb could be used to create
Django models for an entirely different system not written in Python, say
WordPress, and very easily create Django admin for
that system. So I decided to try just that. I would create a WordPress
database, use inspectdb on that database, and create a very simple
alternative admin for WordPress.

Let's get started.

Initializing the Project

Note

I'm going to assume you have WordPress installed and the database set up so
I won't provide any instructions on how to install WordPress here. You can
check out how to install the latest WordPress on WordPress' homepage:
Installing WordPress

Next, let's edit the settings.py. We'll need to add settings
for how to connect to the database. You'll need to set this correctly
so that Django can connect to your wordpress database. We will be putting
the Django tables that we need in a separate database so you'll need
to create that database separately.

You can now inspect the wordpress/models.py and take a look at the generated models.
It will look something like the following:

# This is an auto-generated Django model module.# You'll have to do the following manually to clean this up:# * Rearrange models' order# * Make sure each model has one field with primary_key=True# Feel free to rename the models, but don't rename db_table values or field names.## Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'# into your database.fromdjango.dbimportmodelsclassWpCommentmeta(models.Model):meta_id=models.BigIntegerField(primary_key=True)comment_id=models.BigIntegerField()meta_key=models.CharField(max_length=765,blank=True)meta_value=models.TextField(blank=True)classMeta:db_table=u'wp_commentmeta'classWpComments(models.Model):comment_id=models.BigIntegerField(primary_key=True,db_column='comment_ID')# Field name made lowercase.comment_post_id=models.BigIntegerField(db_column='comment_post_ID')# Field name made lowercase.comment_author=models.TextField()comment_author_email=models.CharField(max_length=300)comment_author_url=models.CharField(max_length=600)comment_author_ip=models.CharField(max_length=300,db_column='comment_author_IP')# Field name made lowercase.comment_date=models.DateTimeField()comment_date_gmt=models.DateTimeField()comment_content=models.TextField()comment_karma=models.IntegerField()comment_approved=models.CharField(max_length=60)comment_agent=models.CharField(max_length=765)comment_type=models.CharField(max_length=60)comment_parent=models.BigIntegerField()user_id=models.BigIntegerField()classMeta:db_table=u'wp_comments'# <snip>

Next, we need to register the models with Django's admin. This is a bit of a pain as it
requires some hand coding. Save this in wordpress/admin.py:

Database Routing

We're going to be using multiple databases so let's create a database router to
tell Django which tables live it which database. We'll be lifting this code
strait from an example from the Django docs (See: Multiple databases)

Let's put this in wordpress_admin/router.py:

classWordPressRouter(object):"""A router to control all database operations on models in the wordpress application"""defdb_for_read(self,model,**hints):"Point all operations on wordpress models to 'wordpress'"ifmodel._meta.app_label=='wordpress':return'wordpress'returnNonedefdb_for_write(self,model,**hints):"Point all operations on wordpress models to 'wordpress'"ifmodel._meta.app_label=='wordpress':return'wordpress'returnNonedefallow_relation(self,obj1,obj2,**hints):"Allow any relation if a model in wordpress is involved"ifobj1._meta.app_label=='wordpress'orobj2._meta.app_label=='wordpress':returnTruereturnNonedefallow_syncdb(self,db,model):"We don't create the wordpress tables via Django."returnmodel._meta.app_label!='wordpress'

Next we'll add the following to our settings.py:

DATABASE_ROUTERS=('wordpress_admin.router.WordPressRouter',)

Create the Django Database

We'll need to create a database on the Django side of things so we'll do
something like this:

$ python manage.py syncdb
Error: One or more models did not validate:
wordpress.wpposts: "id": You can't use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.
wordpress.wpterms: "slug": CharField cannot have a "max_length" greater than 255 when using "unique=True".

You'll notice that a couple models didn't validate. So we'll need to update
them. The first WpPosts needs it's id field updated. Since it's the
primary key we can just add the primary_key keyword to it:

classWpPosts(models.Model):id=models.BigIntegerField(db_column='ID',primary_key=True)# Field name made lowercase.# <snip>

The WpTerms model's slug field can't have a unique=True index with
a field larger than 255 in Django. Since we aren't really doing that much with
the field we can simply remove the unique keyword.

Start the Server

You'll need to login to view the admin. One thing to note is that for the
Django admin we authenticate with the Django user we created when running
syncdb for the first time and not WordPress' users.

Here's what editing a post looks like:

Conclusion

I hope you realized some of the interesting things you can do with the
inspectdb command. We didn't really have to use WordPress. We could just as
easily have used any database application, like Redmine or Bugzilla. There's
also an endless amount of customization you could do using the Django admin to
provide a richer experience. You can get started by reading the Django docs for
the admin: The Django admin site. Have fun!