After converting 70+% of a project at work from Django to TurboGears over a week and a bit, and starting at rewriting an existing TurboGears project of mine to Pylons, quite a few things have interested me about the exercises.
Standard disclaimer about this just being my personal feelings and not some scientific test applies. I've used TurboGears (90% own projects) a lot longer than I've used Django and Pylons, and I've used Django (50% work projects) a little bit more than I've used Pylons (only own projects so far). I also prefer tea to coffee, tcsh to bash, and winter to summer.
...
Templating
TurboGears currently leads in terms of allowing me easily to use my current favourite templating system - Genshi. In fact, it wins in terms of being able to whichever templating system I want it to really easily. The TurboGears expose decorator initially seems a win, but it starts to cause a lot of irritation too - understanding how it works (and remembering how it does) and the bundling of both the expose behaviour and the templating behaviour. There must be a simpler way.
Pylons seems to accommodate other templating languages more and this seems to be better documented, compared to Django.
Databases
The ORM move from Django's ORM to Elixir was the hardest part. Elixir is sufficiently simple a layer above SQLAlchemy that I only ran into one thing that using Elixir prevented me from using a feature of SQLAlchemy, and a patch to fix that was accepted in minutes. So I've got nothing particular to say about Elixir itself.
SQLAlchemy is a bit more verbose and also a lot more precise in terms of queries. Mostly. Django wins in terms of concise (but, frankly, ugly) query syntax, and a really pathological case was something like 4 lines in Django ORM and about 20 lines in SQLAlchemy.
The generative queries in Django are great, and SQLAlchemy offers this and it is going to be moving primarily to generative queries in 0.4.
I had a good interaction with the SQLAlchemy guys about making the pathological case less pathological. I didn't engage the Django guys about some queries that seemed pretty impossible to do in Django, so I can't comment on how responsive they are.
I felt the Django ORM and database documentation is not currently as complete as SQLAlchemy's (at least, for the questions I was asking), but SQLAlchemy's documentation's layout does make finding some things a bit harder than it should be.
In the applications I'm dealing with, the Django admin system really didn't figure into things. I imagine this is the case in most larger applications, and that most developers work from model to "list template" to "view template" to "create template" to "edit template" anyway. That's not to say it isn't a very impressive piece of work, but a non-trivial permissions model, additional business logic, and non-trivial appearance changes does mean you have to start at the beginning again.
I imagine for smaller (especially where only internal people ever see the admin interface) projects, and for people starting out, and for projects where you're whipping up a prototype rather than working on the main development effort, it's a very different story and a huge win. This is still an area Django totally owns.
Controllers/views
Besides the ORM changes above, converting the views/controller code is generally trivial. Most changes necessary are even probably programmable.
Pylons seems to offer the best ability to write the least amount of code while still using highly specialised templates - in other words, not using default views. It does this while keeping out of the way too. I can understand very easily what I need to know about how things are set up before my controller runs. I can easily modify how particular controllers execute works due to the class-based controller method. I can also easily run a certain bit of code before all the methods on a controller with the __before__ method. It feels very comfortable.
The default/suggested function-based method in Django is quite restrictive in this regard. But using a Pylons (instantiate per request) or TurboGears (instantiate once) style class is easy enough to do in Django. Also, for those new to Python (at least at work), it may lead to some very interesting-looking code.
Dispatch
Even when using TurboGears, the CherryPy 2 dispatching system (still the default, but pluggable in CherryPy 3) just so completely turned me off that I quickly found another. Having a centralised URL-based dispatcher like Django and Pylons (using Routes) feels a lot more comfortable to me - it's a lot easier to look at a given URL and find out exactly where it is being fulfilled. And with both (and especially Pylons), you can also revert to a object-style traversal for a "CRUD controller".
Pylons's default route of /:controller/:action/:id is probably not the right way to go for most larger applications, but you can get pretty far with it. Most applications are probably going to want to use an alternate URL scheme (our work one has /requisitions/1/applications/2/interview), and also I think it's more understandable to have the explicit rules for at least controller (you can leave out explicit actions if you have a published scheme around action names, or are using "standard" CRUD or REST controllers style).
Being able to use named routes is invaluable. Being able to change things in one place to have all URLs generated by your application change to the new URL scheme is amazing. And you can also easily still handle the old URLs by keeping the original route, but with a different name. What I would give for the developers I inherited code from at IOL and on KnowledgeTree 1 to have used such a scheme.
One downside of Routes is that you can't really delegate aspects of your application to a separate routes instance, defined in another file closer to the controllers that handle that aspect of the application (like Django does). This isn't a big downside, really, but you might need to put in a bit of effort to have a more "dynamic" application to pull routes from "plugins". And, it can't be said enough, named routes are invaluable.
The released Django versions weren't there yet with named routes when I was using them (but they're in SVN at the time of writing). I also can't say I like the regex-based system (they're yet another thing to introduce developers to). Not that I like the Routes situation entirely. Also, neither really gives you the right types out (although Routes does have a way for you to do this in a separate function). I guess something like FormEncode's validators might work. (But how to make it easy to use?)
Unicode
Django's lack of unicode-throughout caused only a few interesting issues in our work application, but it wasn't nearly as bad as those we had with KnowledgeTree (in PHP, though). Luckily, we (at current work) mostly got away with having utf-8-encoded-byte-strings-throughout, since our application served up exclusively JSON. (Once the work in Django's Unicode branch gets folded in and into a stable version, a whole class of Unicode issues will hopefully be resolved for Python web programmers.)
When using TurboGears and Pylons, I only had unicode issues when interacting with a few external modules (and, irritatingly, some of the Python Standard Library, like email, if I recall).
Filling in forms
Ian Bicking's htmlfill's approach, after quite a bit of skepticism, seems like a decent way to solve the problem it solves (Genshi also has a similar style HtmlFormFiller filter).
htmlfill isn't nearly friendly for simple changes to the way it works (using it verbatim is pretty much trivial), but it ended up not being hard either. Like many projects, we had specific requirements - we had to add classes to tags above the form select fields so we could use CSS easier to do what we wanted.
I would usually just use TurboGears Widgets/ToscaWidgets, but at work we're trying out the "sandbox" pattern for templates, which means we can only pass through primitive types (ie, not Widget objects). I'm still a big believer in the widgets system, but for when you aren't use it, the htmlfill approach seems one of the better solutions.
We weren't using Django forms, so I can't talk all that much about them. (I'm a bit disappointed that newforms doesn't seem to use FormEncode.)
Deployment
On the deployment front, Pylons comes up tops for me - although the configuration syntax isn't immediately obvious. It makes up for this with good documentation - both for deployers and for developers - and with its use of Paste's make-config and setup-app hooks.
TurboGears is a little worse - each individual tool has its own code for finding the configuration file to use, and that tended to mean copying that code verbatim in my little scripts.
Django didn't even set up the project with a setup.py file, so I had to make my own to make packaging and deployment stuff for our sysadmins to deploy things to production. (Django also loses a few points for seemingly not being possible to deploy to whichever web path I want it to live in via configuration. I might just have missed the documentation, but none of the Django users that I know and asked had any suggestions besides editing the urlconf.)
All allow for test cases; Pylons and TurboGears throw a few in for free, which is useful as an example of where to put the tests and what they look like, I guess. Django seems to have put a lot of effort into their test stuff in the last (0.96) release and in SVN, which is promising.
Also on deployment, none have built-in database migration a la Ruby on Rails. I did something a lot simpler and hackier in KnowledgeTree, which mostly allowed us to avoid database-change-related problems in development, and also could reliably upgrade production databases too.
migrate looks like it spent a lot of effort into adding modification code monkey-patched into SQLAlchemy, which will unfortunately probably end up breaking as SQLAlchemy moves forward. The Django options (I saw two or three, I think) look like they could end up working quite well, although I haven't followed which of them is most likely to go in (or if that's already happened in SVN).
The "Conclusion"
In all, while I personally prefer Pylons over TurboGears over Django, none seems particularly the ugly step-child in terms of features, and none the One True Web Application Development Environment.
Each offers different advantages and disadvantages to particular types of work and for different types of programming personalities, mostly based on:
- dispatching methods,
- application composition,
- default templating system,
- ease of use of alternate templating system,
- default ORM,
- ease of use of other ORMs,
- WSGI support (whether hosted or hosting),
- "free" views and admin interfaces,
- "more" integration vs less "in your way",
- closer-to-the-metal,
- easier-to-understand-for-newbies,
- flexible-to-use-for-non-newbies,
- and reusing-existing-components.
Let me stop there, in case I say something silly that'll brand me as a heretic in any of the camps. (That's another post, after all...)
16 old-style comments
Brad Whittington — June 19, 2007 at 10:15 PM.
Daniel Lyons — June 20, 2007 at 07:30 AM.
Kudos to you for your analysis! And let me just say that SQLAlchemy is my favorite database abstraction layer, and I wish it existed for other languages.
wjv — June 20, 2007 at 09:28 AM.
Alen Ribic — June 20, 2007 at 10:43 AM.
"There should be one-- and preferably only one --obvious way to do it."
That is one of the key things I love about Python.
Wish this was the case for Web Application Frameworks too. :-) I guess this is where the WSGI standard will come into play to an extent.
Neil Blakey-Milner — June 20, 2007 at 11:40 AM.
Alen, I think it's useful to have multiple approaches in a field where there is still a lot to be learned about the benefits and drawbacks of various approaches, and how approaches to, say, dispatching, work together with approaches to, say, ORM and templating.
Alen Ribic — June 20, 2007 at 06:07 PM.
1. ORM will be standardised
2. Web Apps will, or already are standardised. (WSGI standard)
3. Templates will be standardised
4. Web Frameworks will exist that implement the standards but of course they will all be different in some way or another, deployment strategy, ease of use, extensibility, lightweight, etc.
5. IDEs, Editors, will have plug-in support for these Web Frameworks.
Seen this happen in my experience with Java Web Application framework scene. Now there are 500 ORM frameworks, a couple of 100 Web Frameworks and countless templating options to choose from. Have fun introducing that to the new developers.
Do you think this will happen? Maybe it’s a good thing f it does? :-D
mike bayer — June 21, 2007 at 06:00 PM.
the only point of standardization for these technologies are servlets and JDBC. Python already has WSGI and dbapi. thats all the standardization we need.
Daniel Haus — June 23, 2007 at 02:31 AM.
There is one feature in TurboGears, which I really appreciate, but you didn't mention it: i18n support. I'd really like to know how Pylons compares in this area. On the other hand you just whetted my appetite to dive into Pylons, so I'll figure it out myself.
Neil Blakey-Milner — June 25, 2007 at 06:07 PM.
Shayan Raghavjee — June 26, 2007 at 03:55 PM.
That's the joy of having a benevolent dictator.
Scott Benjamin — June 30, 2007 at 11:48 AM.
Adrian — July 05, 2007 at 04:04 AM.
How can we take you seriously?
Leon Breedt — July 20, 2007 at 12:43 PM.
Hint: "Neil" :P
brandon salus — August 03, 2007 at 04:50 AM.
Wire — August 17, 2007 at 08:29 AM.
Paul Tall — August 20, 2007 at 07:35 PM.
I especially like the point about Django being great for startups and prototypes. I think for a majority of readers this will be the biggest plus that completely outweighs the cons.
Its the web, average lifespan of a site design is less than a year. Functionality changes monthly.