Jamming with Django

Django Reinhardt

Update: Part 2 with views and a Facebook app

As I mentioned in my previous post, Cameron and I recently did the University of Canterbury Campus Church website using the Django application framework. The sum total of our experience with it going in was that I already knew Python and had gone through half the Django tutorials a couple of years ago when Django was first announced. So this was going to be an interesting experience. We sat down one day, SSHed into the shared hosting account at Bluehost, and opened part 1 of the Django tutorial.

I kept notes as we  went, so we could work out where our time was spent.

Breakdown of development time

Well actually I didn’t keep notes, but that chart isn’t as inaccurate as it may first seem. As you can see, coding time itself was pretty minimal. The rest of the time was spent on wondering why something wasn’t working, then realising we had been using a feature that wasn’t in our version of Django because we were reading the wrong version of the documentation (that their site makes this an easy mistake to make is one of the few criticisms I have of them. And at least they have comprehensive, well-written documentation…). We also spent quite a bit of time bringing down all the sites on our account because we did something stupid with .htaccess files, trying to get Bluehost to do sane things (there’s some very odd and inconsistent configurations over there), and then writing code we then realise we didn’t need. Often because we hadn’t read the Django documentation beforehand, where we would have learnt it already had what we wanted. And Django is pretty cool, and not just because of its namesake.

The majority of the site functionality we knocked out in a single day, producing the content management system for all the static pages, and producing the first version of the sermon database. Django is a model-view-controller framework much like Ruby on Rails, but it’s much more suitable for content-oriented sites like ours. It encourages a modular design of several applications. In our case, the sermon database was an application by itself. If you’re familiar with Django you can skip this next section. It’ll be old news to you.

Modelling the application

Inside the sermondb directory we had two Python files, models.py and views.py. views.py is just full of a couple of functions that render out the Sermon model into HTML by passing them to templates. It’s pretty simple, but I won’t go into it yet because there aren’t any sermons in the live Campus Church site for me to screenshot (I know, I know, we need a development server. We’re working on that). In Django a model is just a class used to describe something you store. There’s often no need to write your own SQL code, although you’re quite free to do so. We decided that there would be two types of objects needing storing in the sermon database. One class we called Sermon, to represent each sermon. The other was Passage, to represent each passage referenced by a sermon. We separated this because we didn’t want to impose any limit on how many passages a sermon could have.

I’m stripping out some helper functions and declarations, we added later, but here’s the contents of models.py for the sermon database.

class Sermon(models.Model):
    speaker = models.CharField(maxlength=50)
    date = models.DateField()
    title = models.CharField(maxlength=150)
    mp3file = models.FileField(upload_to="sermons/",help_text=_("This should be a low quality version (preferably 18kbps), because it will be used for the Flash player."))
    largemp3file = models.FileField(upload_to="sermons/",blank=True,help_text=_("Optional. Preferably a 64kps version."))
    outline = models.TextField(blank=True)
    def __str__(self):
        return self.date.isoformat() + " - '" + self.title + "' - " + self.speaker
    class Admin:
        js = ['js/tiny_mce/tiny_mce.js','js/textareas.js']

class Passage(models.Model):
    book = models.CharField(maxlength=3, choices=BIBLE_BOOKS, core=True)
    startchap = models.PositiveIntegerField()
    startverse = models.PositiveIntegerField()
    endchap = models.PositiveIntegerField()
    endverse = models.PositiveIntegerField()
    sermon = models.ForeignKey(Sermon, edit_inline=True, num_extra_on_change=3)
    def __str__(self):
        if self.startchap == self.endchap:
            return BIBLE_BOOK_DICT[self.book] + " " + str(self.startchap) + ":" + str(self.startverse) + "-" + str(self.endverse)
            return BIBLE_BOOK_DICT[self.book] + " " + str(self.startchap) + ":" + str(self.startverse) + "-" + str(self.endchap) + ":" + str(self.endverse)

If you’re not familiar with Python, don’t be intimidated by the above. The first thing you need to know is that Python is whitespace sensitive; that indentation is acting as the scope declaration that languages like Java provide with { and }. The second is that it’s dynamically typed. Combined it makes for pretty clean reading code. So in the above file we have two classes inheriting from a class called Model, provided by Django. The first thing we do in each is declare what properties each one has. For example, we declare that the sermon has a speaker (or rather, their name in a string of maximum length 50), a date, a title, two mp3 files (low and high quality) with some help text describing them, and an outline. Some of them we allow to be empty by also declaring blank=True. Then we define a method called __str__(), which just returns a string describing the object. This is very useful to have when you actually want to look at a list of such objects. Finally we declare another class inside the Sermon class called Admin. This is Django’s way of letting you control the way the sermon appears in the administration pages. In it we tell Django to include the TinyMCE text editor javascript file (this will enhance our text editing).On the Passage class we declare that it comes from a certain book of the bible, a choice from a big list called BIBLE_BOOKS that I omitted from this sample just for length. It has a starting chapter and verse, and an ending chapter and verse. All these values are just numbers. It also declares a many-to-one relationship with the Sermon class, declaring a It also has a __str__() to describe itself.By running python manage.py sql sermondb, we can see the SQL code that’s getting generated for us from this.

CREATE TABLE `sermondb_sermon` (
    `speaker` varchar(50) NOT NULL,
    `date` date NOT NULL,
    `title` varchar(150) NOT NULL,
    `mp3file` varchar(100) NOT NULL,
    `largemp3file` varchar(100) NOT NULL,
    `outline` longtext NOT NULL
CREATE TABLE `sermondb_passage` (
    `book` varchar(3) NOT NULL,
    `startchap` integer UNSIGNED NOT NULL,
    `startverse` integer UNSIGNED NOT NULL,
    `endchap` integer UNSIGNED NOT NULL,
    `endverse` integer UNSIGNED NOT NULL,
    `sermon_id` integer NOT NULL REFERENCES `sermondb_sermon` (`id`)

We didn’t have to write that. If you’re a PHP coder used to cranking out your own SQL tables and haven’t used any sort of ORM system before, you may not be convinced that we’ve actually saved time here. After all, there’s not a lot of difference in length between the two code samples. Hopefully a single screenshot will be enough to convince you of the merit of the Django approach (and even those who’ve used other MVC frameworks).
Django administration screen
Once you reach this point in the Django tutorial, there’s a pithy little line that says something like “Take a moment to marvel at all the code you didn’t have to write”. This is probably the biggest selling point of Django for sites like this. It comes with an administration section already built in, complete with a user system and a nicely fine-grained permissions system for them. Just look at that form. The date field automatically has a Javascript date selector appear beside it, and the input string will be validated as  proper date. The MP3 file uploads are handled automatically.I’m not sure I can stress enough how much fun Django brought back to web development for us on this project by removing all this tedium. For a site like this where you would otherwise be looking at a content management system, Django gives you the flexibility to custom design your own CMS suited for your own needs. Though I should stress that this doesn’t at all preclude it from non-CMS-like roles. It’s just that this was the role we were asking of it for this site, and it played its part with aplomb.


5 responses to “Jamming with Django

  1. > The rest of the time was spent on wondering why
    > something wasn’t working, then realising we had
    > been using a feature that wasn’t in our version of
    > Django because we were reading the wrong
    > version of the documentation

    When I first started using Django, I was using the stable version (0.96). However, for various reasons I disagree with the developers have opted not to release any new stable versions until 1.0. For this reason, many developers (myself included) maintain a private “stable” branch of Django SVN trunk, and merge patches as needed. It’s a hassle, but the benefits (unicode, CSRF middleware, faster ORM) vastly outweigh the downsides IMO.

  2. I had wondered at the very long time since 0.96. So have you found stability to be an issue with that approach?

  3. Pingback: Jamming with Django (and Facebook too) « Paragraft

  4. Is uploading mp3 files a problem? I’m also doing a redesign of a church website and I’m not realy sure how to deal with the sermons, because they are around 40 Mb. Are there any troubles you ran into?

    Regards, Simon

  5. No, but we’re not using sermon files anything near 40MB. Our low and high quality encodings are 18Kb/s and 64Kb/s respectively. So even an hour of high quality audio would be 28MB, while the low quality version would be about 7.9MB. We use the low quality version in the flash player. My recommendation is to bite the bullet on encoding quality. 64Kb/s is completely adequate for a mono voice recording.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s