Introducing django-upgrade, a tool for upgrading your Django projects

Giddy-up, we’re going to the future!

Update (2021-01-07): My new book Boost Your Django DX covers django-upgrade and many other code quality tools that can improve your code.

Django deprecates a small list of features with every feature release, requiring us to update our projects, which can be monotonous. Today I’m announcing a new tool I’ve created, django-upgrade, that automates some of this drudgery for us all.

The Story

For a while I’ve enjoyed using pyupgrade, which upgrades Python syntax to the latest and greatest. pyupgrade is both fast and accurate, what you’d hope for in such a tool. The idea is to run it continuously, via pre-commit or similar, so that any new usage of old syntax you add gets automatically upgraded.

Last year, after a django-developers discussion, Bruno Alla created a similar tool for upgrading Django projects called django-codemod. django-codemod works well, and I’ve enjoyed running it on a few projects, saving me hours of boring find-and-replace. The only downside is that it’s relatively slow, taking up to several minutes on a medium sized project. This makes it okay for one-off runs, but it’s not suitable for running continuously (except on CI).

django-codemod’s slowness is due to the underlying library, LibCST, which implements its parser in Python. In contrast, pyupgrade is fast because it uses Python’s own C-based parser. LibCST also comes with compatibility concerns - it still doesn’t support Python 3.9, even though 3.10 is around the corner.

I have tried my hand at combining the best of pyupgrade and django-codemod into a new tool: django-upgrade. It’s available today on PyPI, although it doesn’t cover every recent deprecation, and it has a couple known bugs.

I started working on django-upgrade a few weeks ago without announcing it. Since then some people spotted it on GitHub and have chimed in, including Bruno. We’re working on getting django-upgrade to cover all the same fixes as django-codemod.

Eggsample

django-upgrade is a CLI that upgrades Python files in place. For example, imagine we had this model definition:

from django.db.models import Model, NullBooleanField


class Book(Model):
    valuable = NullBooleanField("Valuable")

Django deprecated NullBooleanField in version 3.1, in favour of BooleanField with null=True.

We can run django-upgrade on the file like so:

$ django-upgrade example.py
Rewriting example.py

After this, our field now uses BooleanField:

from django.db.models import Model, BooleanField


class Book(Model):
    valuable = BooleanField("Valuable", null=True)

django-upgrade runs fixers for many such cases, all documented in its README.

With pre-commit

django-upgrade can run every time you run git commit via pre-commit. The art here is to have it run before your general formatting tools, such as Black and isort. This allows django-upgrade to make its changes without worrying about matching your code style.

For example, here’s my current config on one project using django-upgrade (Python tools only):

repos:
  - repo: https://github.com/asottile/pyupgrade
    rev: v2.26.0
    hooks:
      - id: pyupgrade
        args: [--py39-plus]
  - repo: https://github.com/adamchainz/django-upgrade
    rev: 1.2.0
    hooks:
      - id: django-upgrade
        args: [--target-version, "3.2"]
  - repo: https://github.com/psf/black
    rev: 21.8b0
    hooks:
      - id: black
  - repo: https://github.com/pycqa/isort
    rev: 5.9.3
    hooks:
      - id: isort
  - repo: https://github.com/PyCQA/flake8
    rev: 3.9.2
    hooks:
      - id: flake8

Fin

Try out django-upgrade today. Bug reports are very welcome so we can ensure it only makes accurate replacements.

Enjoy,

—Adam


Make your development more pleasant with Boost Your Django DX.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: ,