Django with the Flow

We’ll start by getting the obvious out of the way: if you’re writing anything other than Python code, then you’re doin’ it wrong. With the obligatory “My language is better than yours” arrogant-developer requirement met, I suppose now we can move on to other stuff.

Most of my previous work had been in Rails, but I had been itching to get at Django and just hadn’t found the time. That changed though when LightCastle decided to take our website off of Play and port it to some other framework. Dan and I decided to have a competition to see who could rewrite the site faster; he’d try in Lift (with Scala) and I’d try in Django (with Python). Another coworker considered taking a shot at it in Chicago Boss, but left the competition because it was obvious that I’d win. And really, who can fault a man for a graceful bow-out? Dan ended up being too busy to do much with the competition, so I essentially won by default.

Getting Started

At first I was a little turned off from Django because I just wasn’t catching on to it. The routes were weird. The project structure was stupid. And converting the layouts was a pain. But the more I worked with it and got the hang of it the more I really started to like it. Once it started making sense, I began to see why Django’s developers made the decisions they did.

In case you want to try following along, the code can be found here.

The easiest way to install Django is through pip. (You can also install from source, but I like having greater support for dependencies and general compatibility.) Since I had never installed the framework before, that was my first order of business. A quick call of ‘sudo apt-get install python-pip’ got me the python package installer, and a ‘sudo pip install Django’ had me almost up and running. A subsequent ‘sudo apt-get install python-pip python-dev build-essential’ sealed the deal for all the required dependencies.

Application Vs. Project

djangoProbably the biggest difference between Django and Rails that I noticed right off the bat was the project layout. It turns out that you can rename your directories to suit your needs better, but I didn’t like the default that is created with the django-admin.py, which creates a root-level directory by a name of your choosing, then nests another directory by the same name inside that directory. Essentially, all you need for a functioning Django app is a settings file, so that means you have a lot more freedom to your project layout than I first thought.

The short explanation to Django’s approach to development is that it organizes things by *applications* and *projects*. A project is an overarching concept. So if you’re creating a website for Boeing, the website would be the application, while the project would be something like “Boeing”. The thought is that a project can potentially comprise many applications, from a blogging platform to a content management system. Separating things this way makes it easier to port an application from one project to another if you need to, the reasoning goes.

django-treeFor our project, we ran “django-admin.py startproject lightcastle”, which caused the framework to create an umbrella ‘lightcastle’ directory, with another ‘lightcastle’ directory inside it. A file called manage.py is also created in the top-level ‘lightcastle’ directory, which is used for things like running an interactive shell pre-loaded with your code or starting a local server.

Inside my top-level ‘lightcastle’ directory (also called the project-level directory), I ran ‘python manage.py startapp website’ to create a website application. This creates several files inside a directory it makes called ‘website’: __init__.py, models.py, tests.py and views.py. Admittedly, I didn’t work in the tests.py file at all (Bad developer!!).

A Few Basics

The init files that are created inside the directories allow you to import files in the same directory as the init file as modules into other files. So if you have police_officer.py in a directory that has __init__.py in it, you can import police_officer or any of its individual methods easily. Typically the init files are empty, but you can include code to initialize stuff inside the packages when they’re imported. If you create subdirectories that will have code you want to use elsewhere, you’ll need an __init__.py file present.

The models.py file is where you define your database fields. We didn’t use a database for our project, so I didn’t have anything in this file.

The views.py file was a little tricky for me; it’s not the equivalent to the views directory in Rails as I was expecting. Instead, views.py is where controller logic goes. Actual “views” go in a templates directory. Views have .html extensions, which seems a bit obvious when you think about what a view does: present information. The templates also use a bit of django magic to insert content, set variables or load other html and css files.

Another big difference between Rails and Django is the routing. One thing to remember about Django is that there’s typically very little ‘magic.’ The routing file in Django, called urls.py, is essentially a few custom python functions that take a regex as the first argument (that describes the url path), followed by the name of a handler method. There really isn’t a whole lot of special “Django syntax”, which is a big plus in my book compared to Rails.

Another thing I didn’t like much about Django is that there are usually two routing files for every application in your project: the application-level urls.py file, and the project-level urls.py file. Frankly, I skipped ever using the application-level urls.py file because I had no reason to have one. Essentially your urls are routed through the project-level file first, and then sent to the application-level routing file.

For our website, I just used the project-level routing file because it was easiest and quickest. As i fooled around with the urls.py file, I eventually really enjoyed it. Each route in the file generally points to a views.py controller method that handles which template to use before rendering the “context” as an HTTP response. You can also pass template files in directly as the second argument by calling TemplateView.as_view(template_name=”some_file.html”). That was handy for keeping the view.py file pretty empty, though I probably could have created a small function in there to set a Template and Context.

Conclusion?

I suppose that’s it for my first thoughts on Django. Ultimately, I really enjoyed the framework and look forward to messing with it some more. Keep a lookout for future blog posts about it, as I might end up adding my own Django tutorial to the myriad already available online. I might do one on the WSGI Apache module, too, since it was a bit of a pain to mess with, and I’ll probably include a portion on deploying Django too (turns out capistrano, which is Ruby-based, works pretty darn well).