What is django.contrib?
Since it comes up a lot, I thought I’d spend a bit of time writing up my
thoughts on what
django.contrib really is, and what including a
package in it really means.
The following is just my personal opinion – really; that’s why this is posted here instead of over in the official Django documentation. However, most of the core team discussed this topic at length at DjangoCon, so I’m fairly sure there’s consensus over the rough outline.
In a nutshell,
django.contrib contains optional, de facto standard
implementations of common patterns. That is:
Optional: contrib packages should be removable. You should be able to
rm -rf django/contriband Django shouldn’t break.
Astute readers will note that
django.contrib.contenttypesviolates this rule. This should be considered a bug, I think.
Along these lines,
django.contribpackages ideally shouldn’t get “special access” to brittle internals – anything a contrib package does ought to be possible in an external app. Again, astute readers will notice that we’ve broken these rules in a few places, and, again, that should be considered a bug.
De facto standard: anything in contrib needs to be generally accepted as the Right Way to do something for a large majority of users.
django.contrib.sessionsis one example of this. There are may ways of doing sessions, but the way Django does it – an opaque session key in the cookie, with all the data stored on the backend – is generally accepted as a best practice. Yes, there are other ways of implementing sessions – signed cookies comes to mind – but the approach used by
django.contrib.sessionsgenerally works for most users.
Common patterns: contrib packages should solve problems encountered frequently by real-world web developers. So I’d argue against including something like django-reversion. It’s incredibly cool, and I use it myself, but tracking of versioned data at such granularity is not needed by a large majority of web sites.
Good contrib packages tackle issues that that are not trivial, are bikeshed prone, and are difficult to get right for the common case. We want to prevent folks from needing to decide among seventeen different session frameworks, for example.
django.contrib.gis: it’s not exactly a common pattern exactly,
but if you’re dealing with geographic data, building on existing GIS
tools like PostGIS and GDAL is both the common pattern and the best
practice. So you could say that
django.contrib.gis implements a common
pattern for a smaller class of users, I suppose. More importantly,
though, it’s a tricky thing to get right, so there’s a great value in
having it Just Work.
Of course, there’s a marketing value of having something in contrib. It certainly helps PR to saying that “Django ships with GIS support.” It’s fashionable to look down on PR in the open source world, but it’d be disingenuous to claim that I don’t use the power of contrib as a marketing tool. I certainly do, and I have no qualms about doing so.
However, there’s a danger in bringing something into the core: it stifles future innovation. As soon as we “bless” a contrib package, we drastically reduce impetus to write competing libraries. So, a good contrib package should have general consensus, and should be fairly mature. This is why a schema evolution library won’t be in Django 1.1 despite at least three or four viable alternatives. None of these options are clearly better, and the entire space is evolving fairly quickly. We should wait to “bless” one library until we’re sure we’ve got the right one.
Another downside to contrib inclusion is the coupling of the package’s features to Django’s release cycle. A small package like django-tagging can add features quickly; limiting releases to every six-nine months would retard its spread as users waited for new versions of Django to be able to upgrade.
Including a package in contrib is also a promise of future support. Nothing would be worse than having to remove a contrib package in a future release – we want to add features, not remove ’em! However, adding new contrib packages means more work for committers, so a new contrib package really needs to come along with a new contributor. A group of them, ideally – a contrib package with a bus factor of one isn’t a particularly good idea.