Reader Feedback: Experiences With Django

Welcome to Day 2 of my answering reader feedback questions here in the month of August. I was asked to share my experiences with Django. I will confess that I am a complete n00b when it comes to Python (wrote a few scripts for work-related stuff) and got an early alpha of my baseball road-trip planning site actually working using Django. Sure, it was ugly and very rough around the edges but I got it to work. But I will say this: if I had the opportunity to start working with Python and Django every day (and for the same pay I’m getting now) I would take it in a heartbeat. Seriously.

So why all the love for Django and Python? As a guy who’s used PHP for a very long time, I’m really blind to the syntax of it. All the griping about needle vs. haystack and “PHP is ugly” is just wasted on me. Just so much pissing and moaning by people who should know better and lack the courage to learn the language they way they should. I heard it described by Evan from Identi.ca as being “the working man’s language”. Nothing wrong with that, as PHP is great at Getting Things Done. But Python…Python is vastly different from PHP. It’s about objects. It’s about consistency. It’s about the most amazing interactive interpreter I’ve seen since I was a kid fooling around on my VIC-20 typing in programs from Compute! magazine. Bet you didn’t think I’d been using computers for that long. To shift into bragging mode, I first got paid for programming when I wrote a program for my mother’s school (she’s a teacher) to create the salary grid for the teachers. I was 12. I think.

Also, there’s a dirty little secret about using frameworks that nobody really tells you. After a while, it makes you lazy. You forget how to do certain things because, well, the framework does it for you. The other day there was a tweet from someone asking about the best way to sanitize data before you put it into the database. My first thought was “sheesh, that’s built into the framework” and then I stopped myself and said “you are getting goddamn lazy!”. My response was “try using mysql_real_escape_string(…)” but was also told about using PDO’s binding of parameters as another way to do it. See, those things are all done by the framework meaning lazy old Chris doesn’t have to remember how to do it. Until the time he’s confronted with non-framework code that he cannot simply rewrite as part of a framework. *gulp*. So, since Python and Django are so different from the other frameworks I’ve used it forces me to STOP being lazy and actually learn how not only Django itself works but how the insides of it work as well.

Now, being an old hand at frameworks means using Django is not that big a stretch. I spent a lot time googling around for the “Python (or Django) Way” to do things. Except for a few concepts that are not familiar to a long time PHP guy (generators?) it wasn’t that hard to get up and running with Django. The documentation is awesome, and once you learn to stop writing PHP code in Python and instead embrace doing things the Python way, it becomes a lot easier.

In the end it’s still datasource-backed models talking to some controller code and passing it onto some template. I find the Django templates to be very limiting after my initial experiences and my “PHP is the template” background, and I think that as I get deeper into it I really have to find out how to do things the Django way. What CakePHP calls Model-View-Controller, Django calls Model-View-Template, but it’s really the same thing.

When I compare Ruby to Python, I don’t get the feeling that I will have to resort to dynamic functions and closures to Get Things Done in Django like I see happening in Rails. Besides, they seem to be two very different communities. That’s probably because the leaders of the Django community are so different in temperament and I don’t see the same type of “Django rules, everything else sucks!” mind sets that I saw in the Rails community. Maybe I’m just looking in the wrong places, but I feel more at ease in the Django community than the Rails community, albeit from the outer fringes of it.

So don’t be lazy and complacent about the tools that you use. Go and checkout Django and you might feel the same way about it as I do.

Article Tags >> || ||

What’s In Chris’ Brain - May 2008 Edition

Yes, it’s the day after May Day (workers of the world, unite!) and yes, I only managed to get one blog post done this week. So, it’s time for a round-up of the stuff I’ve been thinking about this week.

  • For all the bleating I did on the CakePHP mailing list this week about using free and open software, the editor waffling continues and I was back to Komodo today. I needed the security blanket of an IDE today after spending way too much time with CSSEdit trying to get stuff lining up properly for a work project.
  • Halfway through a code review project as a side gig. Old code, running on PHP 4.4.8 (those of you following me on Twitter saw me moaning about getting that working with CentOS on Parallels). Needless to say, the code is a mess but I have to actually talk about WHY it’s a mess and what can be done.
  • Trying to see if I can get a caching plugin for WordPress to work with nginx. Not that I ever expect to get Dugg, but you never know. Almost there…
  • Finally managed to get a Django app running properly inside the Google App Engine SDK. Work on that got torpedoed this week by two slow-pitch games…that got rained out…after me waiting around for 20 minutes or so. Next step is to build out my web service for Rallyhat. Should only be a few hours work (he said, not knowing now what he will know later)
  • YASP (Yet Another Side Project) is going to get rolling this weekend. Some cool stuff using Amazon S3, integration with OpenID and PayPal. This time I actually have a plan to make some money. :)
  • I’ve submitted my “Deployment Isn’t A 4 Letter Word” talk for ZendCon.
  • Been updating the IBL web site to use the latest CakePHP goodies, meaning just refactoring a lot of stuff that was garbage and using newer techniques I discover via the mailing list and Nate Abele’s rantings.
  • Special thanks to Travis Cline for sending me a copy of “xUnit Test Patterns” from my Amazon Wish list! Many thanks to a fellow budding Djangonaut for the gift.
Article Tags >> || || || || || || || || || ||

Building Rallyhat: Proof-of-concept Deployment

Well, that went relatively well. As a proof-of-concept for Google App Engine I decided to see what I could whip together today in between domestic duties, looking after the kids and praying that my wife gets better from her massive cold because I’m off to Vancouver tomorrow.

So, I pushed the database for Rallyhat up into the cloud and wrote a super-simple front end just to see if I could do it. You can take a quick peek at http://chrishartjes.appspot.com. To get it to spit something out, you need 3 parameters:

  • home_team — nickname for a baseball team (i.e. Yankees, Red Sox, Padres). Don’t forget to urlencode the spaces with a +
  • begin_date — date in YYYY-MM-DD format
  • end_date — date in YYYY-MM-DD format

It’s a small start, but a start nonetheless. Next step is to configure my app to use the dev version of Django, not the built in version.

Article Tags >> || ||

Building Rallyhat: Importing Schedules

I continue to be impressed with Django as I build out Rallyhat. I have an extreme alpha version working on my laptop, minus the Yahoo! Maps stuff I've been playing around with. The biggest task was populating my database with the schedules for all the baseball teams. I thought I'd share what the import script looks like:

PYTHON:
  1. #!/usr/bin/env python
  2. from django.core.management import setup_environ
  3. import settings
  4. setup_environ(settings)
  5. from rallyhat.www.models import Team
  6. from rallyhat.www.models import Game
  7. from rallyhat.www.models import Location
  8. from rallyhat.www.models import Sport
  9.  
  10. from datetime import date
  11.  
  12. import csv, urllib, time
  13.  
  14. s = Sport.objects.get(name='Major League Baseball')
  15. teamSchedules = Team.objects.all()
  16.  
  17. for teamSchedule in teamSchedules:
  18.     scheduleFile = teamSchedule.schedule
  19.     print "Importing games for " + str(teamSchedule.name)
  20.  
  21.     if scheduleFile != "":
  22.         reader = csv.reader(urllib.urlopen(scheduleFile))
  23.  
  24.         for row in reader:
  25.             teams = row[3].split(' at ')
  26.  
  27.             if len(teams) == 2:
  28.                 if Team.objects.filter(name=teams[1]).count() == 0:
  29.                     print "Adding new team: " + teams[1]
  30.                     homeTeam = Team(name=teams[1], sport=s)
  31.                     homeTeam.save()
  32.                 else:
  33.                     homeTeam = Team.objects.get(name=teams[1])
  34.  
  35.                 if Team.objects.filter(name=teams[0]).count() == 0:
  36.                     print "Adding new team: " + teams[0]
  37.                     awayTeam = Team(name=teams[0], sport=s)
  38.                     awayTeam.save()
  39.                 else:
  40.                     awayTeam = Team.objects.get(name=teams[0])
  41.  
  42.                 gameDate = time.strftime("%Y-%m-%d", time.strptime(row[0], "%m/%d/%Y"))
  43.                 startTime = time.strftime("%H:%M", time.strptime(row[2], "%I:%M %p"))
  44.  
  45.                 # If this game doesn't exist, add it to the system
  46.                 if Game.objects.filter(home_team=homeTeam, away_team=awayTeam, game_date=gameDate, start_time=startTime).count() == 0:
  47.                     locationCheck = Location.objects.filter(name=row[4]).count()
  48.  
  49.                     if locationCheck == 0:
  50.                         print "Adding new location: " + row[4]
  51.                         l = Location(name=row[4])
  52.                         l.save()
  53.                     else:
  54.                         l = Location.objects.get(name=row[4])
  55.  
  56.                     locationId = l.id
  57.                     g = Game(home_team=homeTeam, away_team=awayTeam, location=l, game_date=gameDate, start_time=startTime)
  58.                     g.save()

It's interesting to treat everything as an object after so many years of being in the PHP world where you can mix and match depending on what's been going on in your application. I'm also amazed at how clear the code is. There is some non-intuitive stuff in there, especially when dealing with the saving of model records. It took me a while to figure out I could pass the object containing the model as a parameter when saving a record for a model that it is associated with.

Article Tags >> || ||

Building Rallyhat: First Steps

This will be the first in a series of posts chronicling my building out of Rallyhat, a site that people will use to plan sporting road trips, baseball first. In this first installment I wanted talk a bit about the first steps I too to actually get this thing rolling.

Now, I could easily build this site in CakePHP, so that's not really much of a challenge. I already have one site that runs Cake to keep me on top of things there, and future work-related projects are going to be CakePHP for me as well. So, something else then? I already did the Rails thing, and tore that down to build the CakePHP one. I thought about using Merb, and I'm going to attend a talk at Open Web Vancouver about Merb so I get a better idea about what it could do for me. So that left just one choice.

C'mon, you're not really surprised are you?

One of the things I really liked about Django is the admin app that you get for free. I've already been using it to full some of my support tables for the application, but I wanted to talk about a neat little thing you can do here. In Django, you define the names of the fields in your model in order for the ORM magic to work. Here's a little sample:

PYTHON:
  1. from django.db import models
  2.  
  3. class Sport(models.Model):
  4.     name = models.CharField(max_length=30)
  5.    
  6.     class Admin:
  7.         pass

The Admin stuff is so that those models show up properly in the admin part of the application. So, just like in CakePHP where you can use $this->Model->find('list') to get an array for use in your dropdowns for forms. Django, does this a little differently.

Being totally object oriented, if you drop into the Python interpreter and ask it to return a model to you, you'll get something like this, taken from the Django tutorials:

PYTHON:
  1. (chartjes@jackjack ~/Sites/rallyhat)
  2. >python manage.py shell
  3. Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
  4. [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
  5. Type "help", "copyright", "credits" or "license" for more information.
  6. (InteractiveConsole)
  7. >>> from rallyhat.www.models import Sport
  8. >>> Sport.objects.all()
  9. [<sport : Sport object>]
  10. >>>

WTF? What good is "Sport: Sport object" to us? I want something more descriptive. How about the name of the sport? That's easy. We can modify the Sport model and tell it that when we return the object, return the "name" field instead.

PYTHON:
  1. class Sport(models.Model):
  2.     name = models.CharField(max_length=30)
  3.    
  4.     class Admin:
  5.         pass
  6.    
  7.     def __unicode__(self):
  8.         return self.name

So, when I drop into the shell, I get the following:

PYTHON:
  1. (chartjes@jackjack ~/Sites/rallyhat)
  2. >python manage.py shell
  3. Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
  4. [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
  5. Type "help", "copyright", "credits" or "license" for more information.
  6. (InteractiveConsole)
  7. >>> from rallyhat.www.models import Sport
  8. >>> Sport.objects.all()
  9. [<sport : MLB>]
  10. >>>

Same result, different way to get to it.

Article Tags >> || ||
Want to advertise on this blog? Send email to chartjes@littlehart.net
GTcars Canadian Car Audio TurboDodge Car For Sale Sign
Audi Forum Mustang Forum Dodge Intrepid Miata Turbo
GTscene Pontiac Bonneville


@TheKeyboard is Digg proof thanks to caching by WP Super Cache!