Instant web sites

Published: November 13, 2007. Filed under: Django.

As much fun as it is to write about features of Django which aren’t that well documented, or which aren’t documented (_yet_; one of my goals with each sprint we do is to add to Django’s documentation and bring it closer to being truly comprehensive), there are a ton of features in Django that, while documented, don’t seem to get a lot of use. Which is a shame, because some of them are extremely powerful and make it almost trivial to accomplish otherwise-complex tasks.

So today let’s look at two of these features, and at how they can make your life as a developer ever so much easier.

Introspecting a database…

Some ORMs work mostly by introspection; each time you fire them up, they look at the database you’re using, inspect the tables to see how they’re laid out, and use that as the basis for the features they offer. Rails’ ActiveRecord works this way, for example; instead of writing a Ruby class which describes your database, you write a subclass of ActiveRecord::Base, and it works out most of its relevant attributes by looking for a table in your database with the appropriate name (derived from your class name) and getting a list of its columns. The class itself mostly holds relational information (which is tricky to extract from some types of databases, notably SQLite and MySQL with the default storage engine, and so is expressed in Ruby as a precaution, using has_many, belongs_to and other similar methods) and methods you define to help you work with the class.

By contrast, Django has you define a model class — a subclass of django.db.models.Model — which explicitly declares all of its relevant fields via specialized subclasses of Field, including relational information. Coupled with any custom methods you want to define, and optional metadata inside an inner Meta class, this means that all the information about your model is encapsulated explicitly in the model class. Generally, Python has a preference for being explicit in this fashion, so it’s a good thing, but sometimes it’s nice to have the flexibility of a model that’s mostly “auto-generated” from your database.

And it’s fairly easy to do this; Django allows you to introspect an existing database and will happily generate a set of suggested model classes to match it, which you can then customize to your heart’s content (and, just as in Rails, you’ll typically want to go back and explicitly fill in information about relationships between models, because Django works with most of the same database and so runs into many of the same limitations in detecting relations).

The key is the “inspectdb” command for django-admin.py, which generates output you can save to a file and then edit and — once you’re happy with it — use as your live models. If you’ve got access to add new tables to the database, you can also add some of Django’s bundled applications (like the authentication and admin apps) to your INSTALLED_APPS, and hit the ground running.

This means that it’s fairly easy to take a database from an existing application, and quickly get Django talking to it; if you install the auth and admin applications, you can even run the admin interface directly on top of it, as some people have been known to do.

Yup, you read that right: so long as Django’s ORM can handle the database you’re throwing at it, you can get the admin on top of it with almost no work on your part. If you don’t mind writing a short class to tell Django how it works, you can also make use of existing authentication systems.

…and mining the data

That’s pretty nice in and of itself, but there are plenty of times when you don’t necessarily want to use Django to administer the database, but rather as a way of browsing what’s in it. And sometimes you want both of those. Which is where another documented-but-somewhat-neglected feature of Django comes into play: the “databrowse” add-on. Adrian gave a public demo of databrowse at PyCon in February, and I noticed more than a few jaws dropping as he did so; while the concept behind it is pretty simple, the effect it can have on your productivity is amazing.

In a nutshell, databrowse lets you tell it about a set of models (by “registering” them with databrowse, a trick we’ve seen before), and then it will introspect the model classes (which is something else you know how to do now) to find out what fields they have and how they’re related to each other. Then it simply presents a generic web-based interface for browsing through the data according to the various fields.

Some of the more useful features include:

Pictures speak more loudly than words, so tonight I set up databrowse on my testing server (a Linux computer in my bedroom) using a backup of this site’s database from last week, then took a few screenshots and posted them into a Flickr photo set (when looking at any one screenshot, click the “All Sizes” link above it to see the full-size version). My whole database (well, actually the models I’m willing to show publicly; there’s some stuff in there I haven’t gotten around to exposing or releasing yet) is browsable this way, with lots of handy cross-references.

Taken together with inspectdb and, optionally, the admin interface, this gives you an easy way to instantly get a web-based interface on top of (almost) any database, while writing little or no actual code. And since there are plenty of situations where you need to quickly set up a way to browse a database in an organized way, or administer legacy content, this becomes an extremely potent combination; I remember thinking, as Adrian gave his PyCon demo, that databrowse alone could possibly put some data-mining firms out of business, simply by providing an easy way to get at the contents of a database.

And because it doesn’t provide any models of its own — it’s just some registration code and a set of views and templates — you can use databrowseeven on a database you don’t have permission to add tables to, a useful trick for legacy databases you need to get data out of (see also Django’s serialization framework for an easy way to get the data out of the database, in a structured fashion amenable to importing into another Django-powered database).

There are a lot more use cases for this than just simple exploration of a database (for example, every so often someone asks how to get something like the admin app, but as a “read-only” application), but hopefully a glance over the relevant documentation will give you some ideas for how to put inspectdb and databrowse to work in your own projects; both have been parts of Django for a while now (inspectdb has been around almost forever, and databrowse went into django.contrib not long after PyCon), but they don’t seem to get as much attention as some of Django’s other components. Now that you know (a bit more) about them, go forth and use them boldly.