For the past couple of weeks I've been developing a web application for a client in the Django framework. I looked at frameworks before I started, wondering what I could use to make the development of this project easier.
I have previously mentioned why I abhor PHP even as I developed Mauvespace in that language, but as I started this project PHP seemed utterly unconscionable. PHP doesn't offer anything apart from a dodgy syntax, incomplete object model and an API consisting of a basic CGI wrapper and bindings for a few library functions. Alternatives included my Python framework, but that is a long way from being ready for the mainstream. In fact it's been all but abandoned. I originally wrote it to serve static sites generated from XSL and XML, but then found myself bolting in little bits of CGI scripts and eventually writing a whole ORM.
I had also been looking at Zope, and particularly Zope 3, but Zope has a ridiculous learning curve. It does seem appropriate for more large-scale, extensible development though. It's just that most web application projects for SMEs don't need to be so enterprise that they require unit tests for everything and interfaces saying what each class will do as well as an implementation. I am looking into Zope but I don't expect to find a project that it will be suitable for very often.
But regardless, it looked more appropriate than anything else at this point.
So Django then. I was really impressed with Django initially. However, while I managed to write about half of the application in the first two hours of using it, the rest has drawn out and out.
Django has an excellent ORM. However, it makes the mistake of trying to encapsulate all of SQL in the Python Object Model, which is a kind of antipattern in itself. This happens all the time with ORMs, because while it's easy to make database rows appear to be objects, SQL is designed to do clever things that you don't really want to re-implement in another layer just because you can abstract it. Django's ORM allows users to write code like Game.objects.get(id=the_game) to retrieve a single game. That's lovely. Then it allows you to write code like Game.objects.filter(category=this_category), and it lazily evaluates the query, even allowing you to chain these filters lazily. Which is fantastic.
Then you hit queries like finding objects with date ranges. In SQL this is easy.
SELECT * FROM games WHERE start <= NOW()
In Django this is munged into a horrific double-underscore variable name:
Two sets or more of double-underscores starts performing joins, I believe.
So Django's ORM is a bit unwieldy, but this should come as no surprise because I think many ORMs are like this. One that I have seen that avoid this is Joomla's, where you just hand off an SQL query to the ORM and it just populates objects from the results. Trying to construct complex queries in an object-oriented way is likely to be harder and less maintainably than just writing the SQL query, and so I think this is the best solution in general.
Django's ORM libraries handily integrate with other bits of the system, such as administration screens, meaning almost no work has to be done to make your database rows entirely editable, with configurable permissions, from the admin screen. This is a big win for Django. There's no way you could even contemplate this with, say, Zope, because the ZODB stores arbitrary objects and an admin screen would have to deal with, basically, all of Python.
The annoyances I've found later on in the development of Django is that it's not intuitive. There's a vast wealth of features, and you have to keep looking at the documentation to find them all. There is a lot of magic involved. This is Python's fault, really. Magic is easy to do in Python, and a lot of developers won't have any issues with jumping in and creating magic for ever situation. This means that there is much less code to be written for performing many tasks, but magic has drawbacks. Developers have to understand what the magic is doing for them. In languages where magic is hard, developers can read the code and know, with very little context, exactly what it will do and why. With magic, the object model and even elements of the language can be warped. I'd agree that magic is good for an ORM: it should mean that you can get an object that magically persists. You don't need to know what's going on to make it persist. However, Django goes way too far with the magic. Admin screens are driven by magic variables. The URL configuration is slightly magical. I think permissions are almost entirely magical but I haven't got into that yet.
Then there's the templating language. There is a much-vaunted feature called template inheritance (according to the documentation "the real power of Django templates". In practice this doesn't do anything more than allowing you to stuff some chunk of content into another template. The only situation this appears to be useful is in stuffing the main content of your page into a layout. Woo. It doesn't allow you to do really useful things, like calling templates that create generic page elements with arbitrary content. At least, without creating an inheritance tree of dozens of little template element files in a manner that is likely to be quite unmaintainable. Templates can read Python variables and attributes of Python objects that are passed to them. They can apply "filters" which are written in the core Python code. They can't do anything else, like even computing simple expressions. This is annoying; few other templating languages are so restricted.
In future it's likely that I'll write a Django middleware to do XSL transformation. Then I won't need Django's templating. This doesn't sound like it will be too hard.
I'm currently trying to solve a problem with User authentication. I have a function, tickets_remaining() which I wanted to add to the User authentication class. But I can't do this. I need to create a separate Profile model and retrieve this instead.
So Django: on the whole, I'm impressed with it. The design is easy, extensible, and fast to get going with, but there are some caveats with the API and particularly the templating.