What's Wrong With Django (Slight Return)
Having been a professional Django developer for Mediaphormedia/The World Company the last 5 months has been a happy experience. The vast majority of my experience, especially the changes leading up to 1.0, has been very good. Most aspects of the framework are well thought out and flexible. And my experience with the code quality is that it is well written and (relatively speaking) fairly bug free.
However, because I spend more time with it than I did as a hobbyist before, I've started to notice things I wish were better or would like to improve. So since I'm in a crappy mood, I'm going to (as politely as I can) rant a little about things that haven't been as pleasant. I intend to do something about many of these things as more time permits toward the end of the year. Onward to the list.
- Doctesting Is A Little Painful
- Fixture Hell
- Testing Suite Speed
- Generic Views & Pagination
- SQL Aggregation
- Documentation
1. Doctesting Is A Little Painful
First off, I think doctesting can be pretty great. It's easy to write and easy to hand test. There's very little overhead in producing them. But there are pain points too.
The worst is the line numbers for failures. I have never seen them point to the correct line in the file where the error occurred, so you have hunt down where the failure occurred before you can debug it.
Additionally, loading fixtures in doctests sucks. The "official" way (stated via a ticket) is as follows:
"""
>>> from django.core.management import call_command
>>> call_command('loaddata', 'my_fixture_here.json') #doctest: +ELLIPSIS
Installing yaml fixture 'my_fixture_here' ...
Installed 4 object(s) from 1 fixture(s)
"""
Ouch, ouch, ouch. Nevermind the fact that we're essentially simulating manually running the ./manage.py loaddata my_fixture_here
, this command dumps output to the screen. And if your fixtures change (add/remove objects), you either use the doctest: ELLIPSIS
comment or gain a fresh test failure because it loaded all the data you asked it to load (different fixture count).
2. Fixture Hell
Fixtures make me mad in other ways. For instance, let's say you write two applications, and like a good developer, include tests with fixtures in both. If both apps create a similar model that their data depends on (such as a User
object) with the same primary key, when run together, one primary key will overwrite the data and potentially cause a test fail that doesn't occur when the app's tests are run on their own.
Additionally, you better hope you aren't using a GenericForeignKey
or relying on a ForeignKey
to a ContentType
because at run-time, there's no guarantee that apps/models will be installed in the same order as your instance. In fact, this is frequently not the case, especially between different developers or different instances. The only solution to this I have come up with is to, as part of your tests (either in setUp
or the top of your doctest), manually load up both the desired ContentType
and your fixture objects that depend on them, reset their content_type
to the correct id, then run the tests.
Finally, having to specify ForeignKey
s by id (rather than by name like Rails1) just kinda smells. I understand that it makes for an easier implementation but breaks unnecessarily in real life.
3. Testing Suite Speed
Here's a dirty little secret of testing Django apps. Doctests run way faster than django.test.TestCase
(unittest) tests. The reason is that the TestCase
-style tests truncate and reload all the fixtures in your database on every test method. So if you have a lot of fixtures and a lot of test methods, go brew some coffee/tea and sit for a spell.
4. Generic Views & Pagination
At the very least, date-based generic views seem to lack pagination. This is a borderline atrocity and leads to constantly wrapping these types of generic views. It's not hard but it's more work than it should be, especially if the list-detail generic views support it.
5. SQL Aggregation
This is more of a pony request than anything else, but I really can not wait for some sort of aggregation support to land in Django trunk. I know you can write manual queries (believe me, had to do a bunch of this already) and there are ways to do it now, but I'm waiting for an official API with some backward compatibility. I need GROUP BY and MAX/MIN/SUM/AVG in the worst way.
6. Documentation
Finally, I have a love/hate relationship with the new documentation. I love that there's search and deep links and, to an extent, that the pages are smaller. But finding what you want, especially if you're new to Django or even experienced with it, is ridiculously hard. It takes 4 clicks (minimum) to find the QuerySet
reference, something that many of us use day in and day out. Additionally, using the search frequently sends you to the page you want with an "OLD DOCS OH NOES!" warning when it's actually the new documentation. Frustrating and probably kinda scary to a newbie.
The Bitter End
The best part about all of this, to me, is that everything on this list can be fixed/improved (and I intend to submit patches where I can). Also awesome is that these are my complaints, instead of lower-level details or more common cases. To me, Django is doing much better than other frameworks if this is the best I could come up with for a rant.
1 - HA! Snuck in the obligatory Rails reference!