First Notes on Django

| Comments (7) | Software
I spent yesterday at the IETF coding sprint. The idea here was to rewrite a bunch of the IETF software tools in a more modern system (Django), as well as write a bunch of new tools. I'd never worked with Python or Django before—other than writing test programs—but that didn't stop Cullen Jennings and I from trying to write an IETF charter management tool (still in development). Some initial notes after 15 hours or so of screwing around:

  • This kind of framework really does let you get an app up and running quickly. I figure I could have gotten slightly more done working directly in CGI and Perl, but when you factor in that I didn't really figure out how to get Django to do anything useful until about 3:30, Django seems to come out pretty far ahead.
  • Django embeds a lot of data in the URL itself rather than in arguments. The way this works is that there is a map table from URL patterns (regexes) (Jamie Zawinski, call your office). So, you get something like this:
            (r'^(?P[a-z0-9]+)/$',views.current),
            (r'^(?P[a-z0-9]+)/all/$',views.list),
            (r'^(?P[a-z0-9]+)/fake/$',views.fake_wg),
            (r'^(?P[a-z0-9]+)/add/$',views.add),
            (r'^(?P[a-z0-9]+)/$',views.current),
            (r'^(?P[a-z0-9]+)/all/$',views.list),
            (r'^(?P[a-z0-9]+)/fake/$',views.fake_wg),
            (r'^(?P[a-z0-9]+)/add/$',views.add),
    

    So, looking at the first of these lines, it says that any URL that matches the pattern ^(?P<wgname>[a-z0-9]+)/$ gets handled by the function views.current and the first parenthesized match gets passed as an argument via a parameter named wgname. This is clever, but kind of weird, especially when you realize that these expressions are evaluated in sequence, so there's a chance for collisions. I got bitten by this once already.

  • It's great to have automated mapping from data types to database schema, but it would be a lot better if it could hide the behavior of the relational DB a little better. To take an example, when you want to have a many-to-one mapping, (e.g., cards -> deck of cards), you use a "foreign key", like so:
    class Deck(models.Model):
          brand = models.CharField(max_length=20)
    
    class Card(models.Model):
          suit = models.CharField(max_length=10)
          value = models.IntegerField()
          deck = models.ForeignKey(Deck)
    

    Thinking about this as a data structure, this does two things, one obvious and one unobvious:

    • Create a pointer from any given Card object to the deck it belongs to. This is the forward mapping you'd expect since it's explicitly declared in Card
    • Create a slot in the Deck class called card_set which contains pointers to all the Card objects that belong to the Deck. This is fairly unobvious, since it's not explicitly declared, it just happens automatically.

    Of course, these aren't just data structures; they are mapped to underlying stuff in the database, so this creates some weirdness. To give you an example, I spent about 30 minutes trying to figure out when I created a Deck and then inserted a Card (ok, not really, but analogous structures), I ended up with null pointers in both directions. It turns out that you need to do a save() of the container (Deck) before creating the contained object (Card), otherwise it ends up pointing at nothing. I don't really know why—SQL experts should feel free to tell me—and ultimately had to have Fenner tell me how to make it work.

  • It's pretty clear that there are sophisticated and arguably elegant ways to do jobs (e.g., rendering HTML), but I don't know any of them, so I end up just doing things crudely, hardwiring forms into the HTML, etc. Probably if I were going to really work on this kind of stuff regularly, that would be worth learning.

7 Comments

I think the problem with saving a Card before the Deck is that the Deck doesn't get a primary key until you save it to the SQL database. Without a primary key, there is no way to tell Card which Deck it is associated with. The save() is explicit, rather than implicit, because hitting the database is an expensive operation, and so Django puts you in charge of deciding when you are done modifying an object, and ready to commit it back to the database.

Your Django link needs an absolute reference rather than relative.

"more modern system (Django),"
HREF="www.djangoproject.com/"

Results in this bad link:
http://www.educatedguesswork.org/movabletype/archives/2007/12/www.djangoproject.com/

Leave a comment