Upgrading Django
For the second time in a few years I’ve found myself doing a number of Django upgrades. It’s a good thing: I’m happy the framework I chose to base most of my work on when I went solo has stayed relevant. But this time I wanted to document some of the pain points to make things easier on anyone else going through the same.
First off, this round of updates has been a lot easier than the first: back then I was the point of contact for WebFaction clients who had a Django app but no longer had a developer and those orphaned projects trended ancient. I started working with Django in 2009 with Django 1.1 and that was modern compared to most of the WebFaction projects. The biggest challenge in that group was a project running on 0.96 and using psycopg
. I’d been installing psycopg2
for so long at that point it never occurred to me there was a version before it. And the Internet felt the same way: obtaining a version of Django that old was a challenge (see this thread for how to get versions no longer listed in Pypi). Obtaining a copy of psycopg
proved impossible (I cheated and wound up downloading the folder from the client’s site-packages
directory and using that to replicate the issue they were seeing— definitely not a recommended approach).
General Guidelines
- Use a
virtualenv
or similar concept. If you’re already doing so, great. If you are not, now is definitely the time to start. If you’re doing anything more than a minor upgrade/ working on a project with no 3rd party libraries, you are going to run into some “dependency hell” where you’ve updated to Django 1.NEW and updated all your code to be compatible and then find out some libraries you use aren’t compatible. The best case scenario is where you just need to update the libraries to their latest versions too, but you may have to play around with the versions to make it all work. Otherwise you need to figure out how to replace the library or fork it. - Implicit in my
virtualenv
suggestion is that you also usepip
to install packages andpip freeze
to create a requirements file with the exact versions of the libraries your project uses. - If you are updating from a truly old version of Django, try not to get too hung up on updating to the latest and greatest. You might have to shoot for something a little bit older because the incompatibilities are just too great or there isn’t enough time right now to make it all work. In updating my old site from 1.2 to 1.10 I actually stopped at 1.6 for a while instead. This is another place
virtualenv
is your friend: at one point I had three local environments for my site,tkc
(live),tkc16
andtkc110
. Once I finished the upgrade process, I deleted the first two and renamedtkc110
totkc
and it was like nothing ever happened. Be aware of the long-term release versions and the deprecation schedule when targeting a new version. - If you are jumping from a really old version, do some reading on the release notes to see what new features are available to you. I’m focusing on problems you may run into and backwards-incompatible changes, but one of the major reasons to upgrade are all the improvements you get. Be aware of things like
select_related
,prefetch_related
andonly
for making queries faster. - Change from
render_to_response
torender
. It will make things easier and the former is soon to be removed. - If you get stuck because the changes to project layout or syntax have changed so much, try creating a new
virtualenv
and project with 1.10 (or similar) and then dragging your apps into the project. You will still need to update a bunch of stuff but it may make things a lot easier and help with your future-proofing.
Version-Specific Notes
1.10
- Major changes to middleware
- If you are using MySQL (don’t start now), Django recommends you turn on strict mode. See project note about why and this answer for how
django.conf.urls.patterns
is gone and all view references inurls.py
need to be imported views, not string names.
1.9
- The admin got a facelift!
- Password validation options
django.contrib.sites
is no longer included by default, which can cause aRuntimeError: Model class django.contrib.sites.models.Site doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS
. Just add it back toINSTALLED_APPS
.django.utils.log.NullHandler
was removed, replace the references with logging.NullHandler
1.8
- Major settings change: the new
TEMPLATES
setting replaces allTEMPLATE_*
settings. This is a bit of PITA as you have to move a bunch of things into their new home in that setting. select_related
actually checks that the fields exist on the model you are querying. Previously this just silently ignored the error and you were left thinking you’d improved performance when you weren’t doing anything.django.contrib.formtools
is replaced by an external app- The syntax of
urls.py
files changed: you now have to haveurl(regex, view)
instead of just(regex, view)
, otherwise you will getAttributeError 'tuple' object has no attribute 'regex'
. Make note of the changes in 1.10 tourls.py
files if you’re going to be updating all of them anyway. request.REQUEST
was removed, but you weren’t using that anyway, were you?- Drops support for Postgres < 9.0 (and then 9.0 and 9.1 are dropped in .9 and .10) and MySQL < 5.5
1.7
- Major update which makes database migrations a built-in part of Django instead of relying on 3rd party apps (usually South). See the differences here.
- App loading changes may mean needing to reorder or move some stuff.
- If you are using South, there’s a guide to changing over. One thing that’s not obvious: in addition to removing
'south'
from yourINSTALLED_APPS
, you actually need to uninstall it from yourvirtualenv
or you will get errors likeThere is no South database module 'south.db.postgresql_psycopg2' for your database.
- Default middleware changed
- The syntax/ imports of the
.wsgi
file project use changed. You may run intoAppRegistryNotReady: Apps aren't loaded yet
. See this StackOverfloew thread. RuntimeError: populate() isn't reentrant
- there are a number of possible causes for this (and I’m not sure if there are all 1.7-specific). In my case it was a dumb error where I had an app listed twice inINSTALLED_APPS
.
1.6
- Not much new, simplified setup, swapped a couple of defaults in settings. At least that’s how I remember it and its why I chose this as a halfway step in my own update.
- Actually there’s one change that will bite you if you have any custom managers:
get_query_set
is nowget_queryset
. django.contrib.localflavor
is gone, replaced by a 3rd-party app. Update your requirments file and your references.django.contrib.markup
is gone. You will need a replacement.- There no longer is a
.raw_post_data
attribute on Request objects. Use.body
instead.
1.5
- Introduces a configurable
User
model. A user in the reddit discussion of this post said it caused them problems due to a subclassedUser
model in their project. - The syntax of the
url
tag changed and now the url name has to be quoted. If you use an editor that supports multi-file search-and-replace, you can update all your templates easily. Pro-tip: do this in its own branch or similar for safety’s sake. - Introduces the
ALLOWED_HOSTS
setting. Make sure to do this because it will bite you: the setting only applies ifDEBUG = False
, so you won’t run into errors locally but then your brand-new, updated site will not respond when you post it live. - Deprecates
django.utils.simplejson
. Can do a search and replace toimport json
instead. Unrelated to any upgrade stuff, if you do a lot of JSON processing or a little bit of JSON processing on very big pieces of JSON, take a look atujson
. I’ve gotten a lot of free performance improvements from it. direct_to_template
is gone. Use this solution in its place
1.4
- Lots of good new stuff introduced in this version, the biggest being timezone support.
- The concept of
ADMIN_MEDIA
is replaced by static files app from 1.3 django.conf.urls.defaults
is replaced bydjango.conf.urls
. Update yoururls.py
files accordingly.- Error:
"No modules named six"
- this is an annoying one I ran into with a couple 3rd party libraries. I was testing which versions I could easily update to by updating to 1.2.0, 1.3.0, 1.4.0, etc. The problem issix
was introduced in Django 1.4.2 so don’t update to anything less than that if you’re going to use 1.4.
1.3
- The big thing here is the introduction of
staticfiles
. If you’ve never dealt with that, it’s a sea change and you will need to read the documentation about how to change things. Essentially, this was splitting up the idea of media (user-generated uploads) from your site’s static assets. - Class-based views are also added here. You’re not obligated to use them but if you rely on a lot of generic views, you’re going to need to update those.
- CSRF protection now applies to Ajax views as well.