Monday, June 9, 2014

The Django tutorial app, or, Please, just show me a complete example

When I was evaluating different open-source web development platforms (which currently comes down Django vs Ruby on Rails, as far as I can tell), I tried making the tutorial app in Django.  The documentation is very thorough, but it's also a bit frustrating.  Part 1 is fine; you set up your new application, do some administrative paperwork, and create your data model.  Let's do that now; I want to recreate my "Songs not in 4/4 time" application.  The essential part is:
class Song(models.Model):
    TIME_CHOICES = (
        ('2/4', '2/4'),
        ('5/4', '5/4'),
        ('7/4', '7/4'),
        ('9/8', '9/8'),
        ('12/8', '12/8'),
        ('6/4', '6/4'),
        ('6/8', '6/8'),
        ('3/4', '3/4'),
        ('other', 'other')
    )

    artist = models.CharField(max_length=200)
    album = models.CharField(max_length=200)
    song = models.CharField(max_length=200)
    time = models.CharField(max_length=5,
                            choices=TIME_CHOICES)
I have a thing called a Song, and it has an artist, album, song, all text fields limited to 200 characters, and "time", which has to be one of a few common musical time signatures, such as 2/4, 3/4, or 6/8.

Okay, now to Part 2—wait a minute.

In 2000 and 2001 I worked for a startup called Westside that made ... cloud apps?  It's much harder to describe what they did than it seems like it should be, which might be part of why they aren't around any more, but: imagine Google doc spreadsheets, but with MS Access database structure.  That is, you describe your table (i.e., spreadsheet column headings), and you are done.  Say you are a plumber; this could be your appointment book.  Customer name, date of appointment, contact phone number, that's it.  Automatically, the website makes a form where you can type in new appointments, and a page where you can see all appointments, sorted or filter by any of the fields, paginated if it's too long.  Since date of appointment is a date, it can give you a calendar picker tool automatically.  All the basics of storing and changing data: create, replace, update, delete, query, all done without any code, just specifying the fields of your table.  And you can have multiple tables with relationships: e.g., list of Authors, list of Genres; each book has a Title but also an Author from the list and a Genre from the list.  No code needed.

It flopped for reasons that are obvious in retrospect: who's going to use it?  Small businesses don't want to do even the technical work of signing up to anything, much less thinking about their data model.  They want a full-featured application tailored to their business, and most of those features are not just storing, displaying, and modifying data in a set of tables.  They include credit card integration, pretty screens, advertising, posting to Facebook and Twitter, etc etc.  Big businesses can spend either nothing or way too much, so they are going to buy a million-dollar Enterprise-Grade solution from some consultants.  Web developers might have been a market, but it takes years to build an army of people using a particular technology, and years to make your technology good enough to deserve an army, and the startup had impatient investors.  It was still a much better idea than most things that get millions of dollars, but this was right at the trough between web boom one and web boom two, and that company is long gone even though it absolutely deserved (as much as anything else around) to get bought for a billion dollars.

My point is that that was over ten years ago but when I use the latest state-of-the-art web development platform, it hasn't caught up yet.  In Part 1 of the Django tutorial I defined my model; why is there a Part 2 at all?

It turns out that Django actually does do the same automatic interface creation that the Westside Webs did.  Part 2 walks you through a bit more "glue" code here and there, really fun stuff like:
from django.contrib import admin
from songs_not_4_4.models import Song

admin.site.register(Song) 
And then I have my list page:


And details, editing, creating, deleting:



But.  This whole automated shebang is strictly for administrators, and isn't designed to be exposed to the public as the actual interface to the data.  You have to write a bunch more code to get there.  So, back to the Django Tutorial, part 3.


To build the public-facing part of the website, you need lots more code.  And the tutorial walks you through all of the code.  The sample app, a "polls" app, is a bit more complicated than it needs to be to be a basic example.  And there's a really annoying thing in the tutorial: presenting lots of code that you shouldn't actually use.  I guess it's a pedagogical method, since this is a tutorial, to show you all of the impulsive initial solutions and what's wrong with them.  But there's way too much of that.  And, if you are just looking for an example of the correct approach to work from, this makes the tutorial a frustrating model.  If you go through the whole thing, here are all of the different versions of code you will write for just the "view" portion of the list screen.

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")
Okay, that's just a dummy response to reassure you that things are still working.  You want some real code:
from django.http import HttpResponse

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    output = ', '.join([p.question for p in latest_poll_list])
    return HttpResponse(output) 
But you don't really want to do it that way because "the page’s design is hard-coded in the view ... [instead,] use Django’s template system to separate the design from Python by creating a template that the view can use."  So do this instead:
from django.http import HttpResponse
from django.template import RequestContext, loader

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = RequestContext(request, {
        'latest_poll_list': latest_poll_list,
    })
    return HttpResponse(template.render(context))
Except that "[i]t’s a very common idiom to load a template, fill a context and return ... Django provides a shortcut."  So this, the third example, is the one you should actually follow:
from django.shortcuts import render

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    context = {'latest_poll_list': latest_poll_list}
    return render(request, 'polls/index.html', context)
Okay, got it, use the shortcut.  Great.

Oh, by the way, Django has "generic views" that make automate the most common stuff.  So do this instead:
from django.views.generic import ListView
    from .models import Post

    class PostListing(ListView):
        model = Post
Would it have been so hard to say that in the first place?  Okay, so I want a reference model and the tutorial wants to tute.  But why couldn't they do both?
Happily, Daniel Samuels offers a complete model application with all sample code and no re-writes: https://github.com/danielsamuels/gcbv-article.

No comments :

Post a Comment