Having built a few Django sites now, I'm ninety-nine percent happy with it as a framework. The remaining one percent is a couple issues I have with how it does things, but they're relatively minor. Overall, I'm impressed at how well Django gets all the details right.
These are the things that are still on my wishlist for Django. No doubt some of them are things that Django already does and I just haven't come across them in the documentation yet (and hopefully someone will add a comment to point me in the right direction). Other things are probably impossible given other design constraints. The rest are probably things that just bother me and no one else cares.
In no particular order:
Django's transaction's work on a an autocommit model by default so each time a model object is saved, it happens in a seperate transaction. For web applications, I've always found that it makes far more sense (and eliminates some painfully sneaky bugs) if there's a single database transaction per HTTP request. It's fairly trivial to enable that model in django by including the transaction middleware. I just don't see why it isn't included by default.
Creating a foreign key relationship with Django's ORM effectively does an ON DELETE CASCADE, but it doesn't actually set up the database that way; the deletion is handled in the Python layer instead. If you only ever use the Django ORM to access the database, everything works smoothly and you'd never even notice. However, by not doing the cascade in the database, it makes it more difficult to maintain data integrity if you ever want to modify the database directly with some other tool or language. My understanding is that the reason for this has to do with Django's supporting certain RDBMSs that don't implement cascades. But, dammit, I use PostgreSQL for all my sites and it does support it just fine. SQLObject works with the same broken RDBMSs yet it manages to set up a proper ON DELETE CASCADE when you use it with PostgreSQL.
Django's templates are a thing of beauty. They strike a really nice balance of simplicity and power. But if your template calls a method that raises an exception, it swallows the exception and just returns an empty string. You don't see anything in the browser and there are no tracebacks in the console. On a production site, this is absolutely what you want to happen. But when I'm developing, this behavior masks errors that I would like to see, so I can fix them. From what I've read, there are backwards compatibility issues with having errors make it up to the browser, even in development mode. But it seems to me that it should be possible to get a traceback in the console when running 'manage.py runserver'.
I'm a huge fan of the REST architectural style, so it bugs me a bit to have code like this in all my views:
if request.method == "POST": # do some POST stuff if request.method == "GET": # do some GET stuff if request.method == "DELETE": # do some DELETE stuff # ...
I've seen a few different projects out there to make REST method dispatch smoother, but I'd like to see it in the core. More hooks for supporting E-Tags and conditional requests would also make me happy.
Also see my note on Cherrypy style dispatching below.
Django's 'startproject' and 'startapp' commands are basically doing the same thing as Paste's 'create'. Paste has this wonderful ability to use templates though. TurboGears and Pylons both just use Paste to do their equivalent code generation tasks. So, when I was doing TurboGears at work, I was able to create, by subclassing the TG template, one master application template that included the settings that we pretty much always used, the scripts and configs that we use for our automated deployment system, etc. Then, to start a new TG app, I could just do:
$ tg-admin quickstart --template=ourcustomtemplate
I've actually gone as far as creating my own paste template for starting django projects. So instead of doing 'django-admin.py startproject', I do 'paster create --template=mycustomdjangotemplate' and I get a django project with my custom setup and config. The problem though is that if Django changes the code that startproject generates, I'll have to manually update my Paste template to match it. If Django just used Paste for that functionality, I could subclass the default Django template and pick up those changes for free.
The Django community for the most part doesn't seem too interested in supporting setuptools distribution or distributing eggs for their apps. Setuptools is notoriously difficult to get your head around, but once you do, you can use it to rig up some really slick distribution and deployment mechanisms. I have a script that will, in one step, set up a virtualenv, and install into it all the eggs that I've placed in a directory. Our automated deployment uses that to reliably and repeatably install an application and all its dependencies (the precise versions that I've selected and dropped in the eggs directory) onto our production or staging servers. I never have have the problem of something that works in development breaking in production because prod had a slightly different (and incompatible) version of some library installed. You can rig this sort of thing up with distutils packages too, but eggs make it much easier. Like with Paste templates, I already use this approach with Django, but it involves the extra step for me of generating eggs for Django and the third-party Django apps I use. The plugin mechanism that setuptools provides is also a thing of beauty and I can imagine quite a few ways that it could simplify the building and deploying of reusable django apps.
sorl.thumbnail rocks. It's always the first Django app I add to a project (every project I work on seems to involve image uploading at some point). It is my opinion that sorl.thumbnail, or something that provides equivalent functionality should make it into the core since it's such a common need.
Django's regex dispatch in urls.py is very powerful and flexible. It's basically the same as Routes, but the plain regex syntax seems much more sensible to someone like me (with years of Perl experience) than Routes' custom rules syntax. Still, it's overkill for a lot of applications. I'm still a fan of Cherrypy's approach of mapping the URLs to a tree of controller objects. So '/foo/bar/baz/?p1=v1' turns into a call of the 'SomeRootObject.foo.bar.baz()' method with p1=v1 arguments. It may look really restrictive, but in my experience with TurboGears, you almost never need to do anything more complicated than that (and Cherrypy does have mechanisms to let you do more complicated things for those few times when you need to). On my todo list is to write a cherrypy style dispatcher for Django (not to replace the default urls.py approach, but to augment it) or just figure out how to shoehorn cherrypy's dispatcher directly into a django project. I know that probably no one else will care, but it will make me happy.
Oh, wait, Django already has one. Nevermind.