Django: A Python web application framework

I’ve known about Django for quite a long time, in fact, I knew about it before it was released. If memory serves right, it was mentionned by someone on the Ruby on Rails mailing list and I had briefly checked it out, but without anything to download, I quickly dismissed it.

Django has now been available for a year (it was released in July 2005, right?) and this week I found a video presentation by one of the lead developers, and I was intrigued enough to check it out. Many folks on #grasshoppers also quite like it, so their praise for it played a part in my trying it.

At first, I didn’t know what Django could offer that Rails couldn’t: Rails has been around for a longer time, has a larger community and the Ruby language is more flexible in creating DSLs (domain specific languages) which is really what makes Rails be Rails. The video presentation showed some important differences in philosophy and in history that make the two frameworks similar, but aimed in slightly different directions.

Both frameworks were extracted from real world applications as David Heinemeier-Hansson says; the authors were simultaneously creating a product and the tool to create this product. Ruby on Rails was extracted during the creation of Basecamp, a collaborative project management system. Django was extracted while creating a rich information web site, Lawrence.com. The difference between the two projects is that Django was used more in a “web site” environment, where people of the Lawrence Journal entered the content and the users viewed the content while Rails was used to create an application where users would create and view the content.

This slight difference shows in some ways, most visibly, in my opinion, in Django’s (incredible) automatic admin interface. In Jacob’s talk at Google and in Adrian’s talk at Snakes And Rubies, both explained the need for this interface. Basically, their boss would say “Yeah, hi, wouldn’t it be like cool if we had a section on X subject? Oh, if you could be finished in three hours, that would be grreat.” (I decided to imagine their boss as Bill Lumberg ;)) Being in the world of journalism, they faced a lot of deadlines, and therefore all the long, boring, repetitive tasks had better be automated. That’s what they aimed to do with the admin interface. They explain that they would come up with the database model for the new site or section that was needed in a few minutes, activate the admin interface, which takes somewhere between 2 and 10 lines of code depending on what features you want and the employees of the journal could start entering data into the database while they went off and began coding the views and the design guys would work on the templates that the users of the site would see. So that feature was pretty crucial in their case, even if it has some limitations. Ruby on Rails doesn’t have that feature, it has scaffolding, but it’s nowhere near the level of sophistication of the admin interface. Note that someone could write a plugin or a generator for Rails to make such an interface, but the fact that it’s not in the core of the framework shows the different views between the Django and Rails people. Django also automates a few other things like authentication and authorization, RSS feeds, comments on pages and generic views. Basically, all features that the Django guys needed to stay competitive.

I finished the Django 4-part tutorial. The example application was simple enough that it was easy to follow, but it used two tables to introduce foreign keys right off the bat. It doesn’t go into how to make links from one page to the next like in Rails, because they don’t have specialized function for that, you just use a regular <a href=”"> to go from one page to the next. The template language — I’m not sure if they have officially named it — is simple, but it’s not Python code, it’s something like Python code. That’s different from Ruby for many reasons. One, you need to learn something new, like to use endfor and endif on your blocks, you omit parentheses from function calls, etc. That’s different from Ruby on Rails which uses ERb which puts the full power of Ruby in your views. However, by being much simpler, the Django template language is probably more accessible to the designers of the pages. They learned to make a link with <a href=”"> and that’s what they have to use. In Rails, most people would use the link_to method. In his talk at Google, Jacob mentionned that the language was liked by designers because of its simplicity, even if regular developers feel uncomfortable with it sometimes, as it breaks down the DRY principle; designers don’t care about that. He also mentionned that he thought the template language wouldn’t become more complex and that in fact, it was already a tad too complex.

Django has a few warts though, but those are more related to Python I guess. For example, the filter() method in the field lookups really bugs me, because you pass arguments to the method, not a block like you would in Ruby. So you must say things in a non-natural way:

# Django
adults = Person.objects.filter(age__gte=18)

# How it would look like with Ruby
adults = Person.objects.filter { |p| p.age >= 18 }

So it’s pretty simple in both cases, but what if you want adult or males? In Ruby, that’s easy, you add or p.male? (assuming you have a male? method) and that’s it. I’m not familiar enough with Django to know how they would do that. I think this shows that having blocks can make some code shorter and easier. I’m also not a fan of the double underscore thing in Django and it could use a more organized API documentation.

In spite of its flaws, I think Django is pretty cool, and if I had to build something like an intranet, I think I would pick Django over Rails just because Django has the nice admin interface to get things started fast and user permissions right out of the box.

3 Responses to “Django: A Python web application framework”

  1. ddipaolo Says:

    If you want to match another field in the database you use the same syntax and chain the filter calls together. In your “what if you want adult or males?” example you would use:


    adults = Person.objects.filter(age__gte=18).filter(gender__exact='male')

    In fact, the shortcoming with yours is that you are actually filtering Ruby objects whereas the filter() call on Django’s QuerySet object type does the filtering on the database side. The filter call creates the proper WHERE clause for whatever database backend is being used and performs the SELECT there. No actual Python objects representing the model are created until you pull out an individual row or rows.

    Having blocks does not add any functionality that doesn’t exist in Django, since all you are doing is essentially filtering the Ruby objects with blocks of Ruby code instead of generating Ruby code that creates SQL-based filters that will give you the results you want. The Django filter command is much faster than if I did:


    people = Person.objects.all()
    adults = [person for person in people if person.age &gt;= 18]

    In fact, I naively have several such list comprehensions in my code for my poker tracker app right now and I need to refactor them to use the much more efficient filter command that’s built in.

    Also, the double underscore is used because there shouldn’t be any field names with double underscores, so there will be no confusion where the field name ends and the conditional begins.

  2. Vincent Says:

    Strike: would doing x.filter().filter() add different clauses to one SELECT statement in SQL or would the second filter() work on the subset return by the first one?

  3. Adrian Holovaty Says:

    Frankly, Person.objects.filter { |p| p.age &gt;= 18 } strikes me as non-natural. But maybe it’s because I’m a Python programmer, not a Ruby programmer.

    To answer your question, x.filter().filter() adds multiple clauses to the SELECT statement. You can chain filters and all that stuff, and it only does one SQL statement. Example:


    Person.objects.filter(age=18).order_by('first_name')

    One particularly cool feature about this is that it’s not actually evaluated until you need the results. So you can add to your objects incrementally:


    query = Person.objects.filter(age=18)
    if some_condition:
    query = query.filter(name='Joe')
    # Database query hasn't been executed yet.

Comments are closed.