django-planet
March 20, 2023

Upgrading Django Projects - Introduction

in blog Screaming At My Screen
original entry Upgrading Django Projects - Introduction

One questions I am asked quite often is how to upgrade larger projects to a newer Django release. While upgrading to a newer minor release is usually really easy and does not require much work it can become a bit harder upgrading to a new major release. My oldest project points back to 0.9x, so it has seen some upgrades, and there are certain patters that proved to work pretty well.

Upgrading a Django project is not harder or easier than upgrading any other project of the same size which is using a big third party framework as base and smaller libraries for certain parts of the system. I will try to start with general and simple principles on upgrading projects and move to more Python and Django specific topics later on.

Before upgrading you should ask yourself if you have to. Let us assume your application is running fine and you are on a release that still gets bugfix and security updates. You could upgrade to get new features that eventually help you down the road. Or you could implement a new feature that separates your application from all competitors. The answer in that case is pretty obvious, isn’t it?

You should keep in mind that the longer you wait with upgrading the more work it can become. There are three situations where you should always upgrade:

  1. No more bugfix and security patches for your Django version
  2. There is a new feature that will help you getting stuff done
  3. No more bugfix and security patches for your Django version

My general advice would be: try to stay as close to the latest release as possible. Maybe not on the first day of the release, maybe not before the first minor release of a new major release. But try to stay up to date with the Django release cycle. I have to admit that personally I have a hard time breaking out of the old „never upgrade to a .0 release“ habit, but the Django dev team does an amazing job with releases and proved that this is eventually not the best strategy for a Django code base.

RemovedIn Warnings

Start to love them, they will save you a lot of pain. Django is telling you pretty early when a feature will be removed. If you take care of those warnings when they first show up, upgrading will be so much easier.

Let us say you are currently running Django 1.7. Once in a while you should run your tests and server with the -Wd option to show silent warnings. If you happen to work on a part of the code where such a warning is raised - fix it. If you are not working on any code related to this warning just ignore it. It means the feature you are using will be removed in Django 1.9, so there is plenty of time.

A different story are RemovedIn warnings which are shown when you do not use -Wd. This means the feature will be removed in Django 1.8. Fix the code immediately, no matter on which part of the system you are working.

If you are not using any features that have been removed when upgrading you already took care of a major pain point. And fixing deprecation warnings when they first show up is usually pretty painless.

Deprecation Timeline

The same process as for RemovedIn warnings applies to the deprecation timeline you can find on the official website. If you happen to work on a part of the codebase that uses a feature that will be deprecated in current release + 2 then update your code if possible. If you see anything that will be deprecated in the next release always update your code, even if you are not directly working on this specific part of the system.

Updating things that will be deprecated in the next release can become part of your upgrade process. If you are upgrading from Django 1.6 to 1.7 you could, as a part of the process, just update the code that is flagged to be deprecated in 1.8.

Forking Dependencies

Sooner or later the following will happen: Your application depends on a third party package to work with certain, usually the one you are using, Django version, but it is not updated to support the newest Django release yet or it is not actively maintained anymore. If you do not want to reimplement the whole package you always have the option to fork or vendor / bundle it. Do not be afraid of forking a package and bundling it with your source, nearly everyone will have to do this at one point, just get used to it.

Usually this means just putting it in your project directory if you are lazy and you are done - this works because you can directly import packages from there, so you do not have to change any code. Putting it in a dedicated directory would be the better option though - most of the time this directory is called „vendor“ or „third_party“.

From there on you can freely edit, change and update the package to make sure it is compatible with the latest version. Do not forget to remove it from your requirements.txt, no need to install it if you ship it with your codebase.

When doing those changes, like fixing compatibility or updating something, please consider opening a pull request if the project is hosted on GitHub, ButBucket or another VCS hoster that supports pull requests or sending the maintainer of the package a patch. There are likely other people who need those updates, too. Relying on other people to do this is tricky, most of the time changes are made, forks are created, but nothing is contributed back to the original project with the result that not one or two developers but eventually hundreds waste their time changing exactly the same two lines of code.

From time to time a package is not maintained anymore or the maintainer refuses to fix obvious bugs or does not even acknowledge them as such. If this happens it is sometimes easier to just create a fork you will maintain yourself or make it part of the your project. If this really makes sense is hard to tell - depending on the size of the package you put a lot of work on yourself, eventually forcing yourself to maintain a package with many bugs you did not notice yet. Sometimes it is easier and in the longterm the better solution to just search for an alternative package. It is rare that there is only one package providing a certain functionality.

Release Notes

Once you decided to upgrade, took care of the dependencies and warnings, ran pip install -U django and made sure your code is working it is time to read release notes. Too often have I seen complex, weird, scary (or all three together) code that kind of tries to solve what Django already ships as a working, well tested util, function or method. New stuff is added constantly, try to stay current with your knowledge about the different parts of the framework, especially the view layer and the ORM.

Sometimes it is something trivial like being able to give migrations a name - making it easier to identify them when browsing the files - but in the long run things and stuff accumulate and provide real value.

Conclusion

While this is a pretty high level overview of the whole process it should answers the, in my opinion, most important questions I was asked. It is by no means a complete or Django specific guide - I am planning to go into more details in future posts walking through some more complex scenarios I encountered.

The most important advice I can give you is that you should always upgrade when there are no more security and bugfix releases for your Django version. Maybe it is scary if you are doing it for the first time. Maybe you will mess up a little bit. Maybe it will take some time. But that should never be the reason to stay on an outdated version, eventually jeopardizing your or your customers data.