Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, PHP, and whatever else I happen to feel like hacking at.

How useful is Django?

Jerry Stratton, May 17, 2008

I was recently asked “how do you find Django?” after mentioning that I’ve been using it to maintain my web pages.

The answer is, “somewhat conflicted”, but on the balance very useful. It is very, very easy to go from an idea to a working database/application combination. Just this weekend I was adding a couple of new albums to my albums database and I decided it was time to convert it to Django/SQLite. The Django setup was a snap. The import from FileMaker (using appscript) was a snap. Getting the multiple columned PDF that I take to record shops was fine. I spent more time than I should have trying to figure out how to make the third page rotate 180 degrees before giving up, but it went pretty well.

One of the reasons I (use to) use FileMaker is because it’s easy to set up layouts, but with CSS editors such as CSSEdit, creating nice layouts in HTML is pretty easy, too. With CSS3 support coming on line, even multiple columns broken up across a page will be possible, removing the necessity for tools such as reportlab.

There appear from the outside to be two poles in the Django project: easy to use and quick to set up on one end; and an attachment to some conventions that tend to break outside of Django on the other end. It’s the former pole that appeals to me. The prime examples of the latter are probably autodating changes and discouraging NULL in string columns but not other columns.

The current stable version of Django makes it easy to keep track of when a record changes; the current unstable version does not, and the likely recommendation appears to be for people to override each model’s “save” method. Having to override methods for basic functionality takes quite a bit away from Django’s ease of use. It may be fixed before the next stable version, though.

The NULL issue is a bit more complex. The Django model reference describes it this way:

Avoid using null on string-based fields such as CharField and TextField unless you have an excellent reason. If a string-based field has null=True, that means it has two possible values for “no data”: NULL, and the empty string. In most cases, it’s redundant to have two possible values for “no data;” Django convention is to use the empty string, not NULL.

The problem is that in all SQL databases, the empty string is never “no data”. Only NULL means “no data”, which means that Django will die horribly if more than one record has no data in a unique string column. For all practical purposes, without overriding a model’s “save” method string fields can’t support SQL’s option of allowing no data in a unique column. For that matter, even in Django the other field types do use NULL values to mean “no data”.

I can’t see any place where the combination of null=True, blank=True, and unique=True will ever make sense using the empty string to mean “no data”.

In general, though, Django is extremely flexible. I’ve integrated Mako into the Django template language, so that I can use Django’s easy template system when I want it, and then fall right into full-on Python when I need a full programming language.

Here’s a simple example of what makes Django so cool even for people with a minimal familiarity with the command line, and the problems I have with it:

[toggle code]

  • class Post(models.Model):
    • title = models.CharField('Title', maxlength=120)
    • description = models.TextField('Description', maxlength=500)
    • slug = models.SlugField('Unique Reference', unique=True, maxlength=25, prepopulate_from=("title",))
    • content = models.TextField('Content', blank=True)
    • parent = models.ForeignKey('self', blank=True, null=True)
    • filename = models.CharField('File Name', maxlength=30, blank=True, null=True)
    • offSiteURL = models.CharField('Off Site URL', maxlength=160, blank=True, null=True, unique=True)
    • added = models.DateTimeField('Date Created', auto_now_add=True)
    • edited = models.DateTimeField('Last Modified', auto_now=True)
    • publishon = models.DateTimeField('Publish on date', editable=False, blank=True)
    • class Admin:
      • list_display = ('title', 'edited', 'publishon')
      • list_filter = ['edited', 'added']
      • date_hierarchy = 'edited'
      • search_fields = ['title','slug','offSiteURL', 'filename']
      • pass
    • class Meta:
      • unique_together = (('parent', 'filename'),)
      • ordering = ['title']

This is all I need to create a model of a post. If I then type “python manage.py syncdb” it will create the MySQL or SQLite tables for me, set up any cross-reference tables it needs, set up an administrative interface for adding and managing posts… it’s awesome, except that it will probably fail after two or three posts, depending on the ratio of off-site to on-site postings. What’s going to happen is that if there are more than one off-site postings for a given “parent” post, the “uniqueness” requirement that there be no two filenames with the same parent will fail in the SQL database. The SQL database will complain, Django won’t know what to do, and it will throw up an error every time I try to edit or add a page.

Similarly, as soon as more than two items in the database don’t have an off-site URL, the same thing will happen: Django’s notion of “no data” will conflict with SQL’s, and the whole thing comes crashing down.

“No data” has a very specific meaning in SQL; it means “we don’t know what this is, it may not even exist”. Django’s meaning of “no data” appears to be something else.

More good stuff

The tables that Django creates are easy to recognize for what they are, making it easy (if you’re so inclined) to write separate scripts to access the data without using Django. However, the Django API is so useful I almost never do; my command-line scripts use the Django API to mass-change data.

Adding new features is easy, as long as the feature can be conceived of as either a collection of SQL tables, a method on a table’s model, or as a template tag for insertion in a template file.

More bad stuff

Other than minor concerns with the direction of Django, the only other big problem I have with Django is the general lack of documentation for Python projects. This is not specifically a Django problem. Django itself is well-documented, comparatively (as is Mako), but other Python modules (Paramiko, Reportlab) can be a real pain to figure out, and the Python documentation itself tends to suck. It has few examples and what examples it does have tend to be a restatement of the syntax definition—often using non-working snippets. Coming from the php.net/manual documentation, Python’s “elusive documentation” is a real roadblock. I still haven’t figured out what encode and decode really do, despite their seemingly obvious names.

On his blog, Russell Beattie wrote “I honestly don’t know how anyone ever understands how to get anything done beyond a basic for loop, as every time I try to look something specific up, it’s either NOT CLEAR or NOT THERE.”

I agree with Russell on this. I stumbled into Python due to, first, Mailman, and then later Django. I only started using Django because I’d heard about how cool Ruby on Rails was, but when I downloaded Rails the development server kept crashing after every connection. So I Googled “PHP Rails” and then, on a lark, “Python Rails”. That brought me to Django, which worked. Django’s documentation also worked. If not for Django I would never have started using Python for serious projects. I still sometimes question whether I should be using it—usually when I’m knee-deep in a Python library with no working documentation.

Good documentation is especially important in a project like Django, which is still undergoing major changes. The general advice in areas such as newforms where Django isn’t as well documented as it should be—look at the source code—will result in finding spurious solutions, and hard-coding side-effects into your code that were never meant to be used publicly.

That said, there’s nothing else I’d rather be using to manage my site and other data right now. If it turns out that the next version of Django doesn’t work for me, I’ll happily continue using the current 0.96. It’s pretty damn cool.

  1. <- Apache XML
  2. Mac OS X Spaces ->