Django Version Upgrade Checklist for Production

Upgrade Django in production safely: plan the target version, read release notes, fix deprecations, upgrade dependencies, test, deploy, and roll back if needed.

Problem statement

Django upgrades are routine when done regularly and painful when postponed. A project three feature releases behind faces removed APIs, third-party packages that no longer support the old version, and a large change surface to test at once. Meanwhile, an unsupported Django version stops receiving security fixes, which eventually becomes a compliance and safety problem.

The upgrade itself is rarely complicated; the risk comes from doing several things at once (Django, Python, dependencies, and infrastructure) and from discovering behavior changes in production instead of in tests.

This checklist covers planning, preparation, execution, and verification for a Django upgrade, whether it is a patch release or a major version step.

Upgrading Django safelyRead notesdeprecationsUpgradeone version stepTestsuite and warningsDeploywatch closelyGo up one feature release at a time, clearing warnings before the next step.

Quick answer

  1. Know your current and target versions, and check Django's supported versions and end-of-life dates.
  2. Upgrade in steps: one feature release at a time, with dependencies and Python handled separately.
  3. Read the release notes for every version you pass through, especially backward-incompatible changes and removals.
  4. Run tests with deprecation warnings as errors on the current version first, and fix what appears.
  5. Verify third-party packages support the target version.
  6. Upgrade in a branch, run the full test suite, check --deploy, and makemigrations --check.
  7. Deploy to staging with production-like data, smoke test, then deploy to production with a rollback path.

Checklist

1. Plan the target version

What to check:

python -c "import django; print(django.get_version())"
python --version
one feature release at a time4.2 LTSstart here5.0fix deprecations5.1fix deprecations5.2 LTSa safe resting point6.0when dependencies allowEach step surfaces the warnings that the next step turns into errorsdjango-deployment.com
Skipping releases hides deprecation warnings you needed to see.

Decide the target based on Django's release cadence: feature releases roughly every eight months, with every third release an LTS supported for about three years. Two common strategies:

  • LTS to LTS: fewer upgrades, larger steps, longest stability per version.
  • Follow feature releases: smaller steps, earlier access to improvements and deprecation warnings.

Either way, upgrade through intermediate versions rather than jumping several releases at once, so deprecation warnings guide you. Check the supported Python versions for the target Django release; a Django upgrade sometimes requires a Python upgrade, which is a separate change to sequence deliberately.

2. Inventory dependencies

What to check:

pip list --outdated
pip show django-rest-framework django-storages celery 2>/dev/null | grep -E "^Name|^Version"

For each significant package, confirm the target Django version is supported (changelog, classifiers, or issue tracker). Packages to check carefully:

  • Django REST Framework or another API layer
  • django-storages, django-allauth, django-filter, and other widely used add-ons
  • Celery and its Django integration
  • database drivers (psycopg, mysqlclient)
  • anything that touches internals: custom middleware helpers, admin extensions, template libraries

Unmaintained packages are the most common blocker. Plan replacements before the upgrade, not during it.

3. Fix deprecations on the current version

Before changing versions, run the test suite with warnings visible:

python -W error::DeprecationWarning -m pytest
# or
python -Wa manage.py test
make deprecations loudpython-Wdmanage.py testPYTHONWARNINGS=error::DeprecationWarningpython manage.py check121Show every DeprecationWarning once, with its location.2Turn them into failures, for CI.Fix them on the current version, where the old behaviour still worksdjango-deployment.com
Warnings from third-party packages tell you which dependencies to upgrade first.

Django emits RemovedInDjangoXXWarning warnings for APIs that the next versions remove. Fixing them while still on the current version means the upgrade itself changes very little. Address your own code first; for warnings from third-party packages, check whether a newer package release fixes them.

Deprecation warnings also appear at runtime, so a staging environment with -Wa or logging configured to capture warnings can reveal code paths tests miss.

4. Read the release notes

For every version between current and target, read:

  • Backward incompatible changes: the section that breaks upgrades.
  • Features removed: APIs that no longer exist.
  • Deprecated features: what will break in the next upgrade, useful for planning.
  • Database backend notes: minimum database versions can change.
  • Security notes: behavior changes that affect settings or defaults.

Keep a short list of items that apply to your project, and check them off as you handle them.

5. Upgrade in a branch

git checkout -b upgrade-django-5.2

Update the pin and lockfile:

# pip-tools
sed -i 's/Django>=5.1,<5.2/Django>=5.2,<5.3/' requirements.in
pip-compile requirements.in
pip install -r requirements.txt

# or uv
uv lock --upgrade-package django
uv sync --locked

Then work through failures in this order: imports, settings, models and migrations, views and forms, templates, tests.

6. Check settings and migrations

python manage.py check
python manage.py check --deploy
python manage.py makemigrations --check --dry-run
python manage.py migrate --plan

Notes:

  • new Django versions sometimes generate migrations for existing models (for example changed field defaults or index naming); review and commit them deliberately
  • makemigrations --check failing after an upgrade means the new version wants schema or state changes; inspect with sqlmigrate before applying
  • settings that changed defaults may need explicit values if you relied on the old behavior

7. Run the full test suite

python -W error::DeprecationWarning -m pytest -x

Pay attention to:

  • authentication and sessions
  • forms and validation messages (wording can change)
  • template rendering differences
  • admin customizations
  • timezone and date handling
  • serialization formats in APIs

Test coverage determines how much confidence this step gives. For areas without tests, plan manual verification in staging.

8. Staging deployment

Deploy the branch to staging with production-like data:

  • run migrations and record their duration
  • exercise critical flows manually: signup, login, password reset, checkout or the equivalent core action, file upload, admin edits
  • check background tasks and scheduled jobs
  • watch logs for new warnings and errors
  • compare page and API response times with the previous version

Leave the upgrade in staging for a few days if you can; some issues only appear with time or scheduled jobs.

9. Production deployment

Plan the release like any other risky deploy:

  • deploy during a low-traffic window with the team available
  • run migrations as a separate, monitored step; ensure they are backward compatible if old code may still run
  • keep the previous release artifact ready for rollback
  • monitor error rates, latency, and background queues closely for the first hours
  • have a rollback decision point and criteria
the upgrade release1Staging ran it for daysreal usage, no new errors2Back up the databaseupgrades can add migrations3Deploy like any releasesame pipeline4Watch errors closelyfirst hour5Keep the old lock filerollback is a redeployAn upgrade is a normal release with a larger diff, not a special eventdjango-deployment.com
Contrib app migrations are usually backward compatible, but check before relying on it.

For containerized deployments, the previous image tag is the rollback path. For virtualenv-based deployments, keep the previous release directory with its own environment.

10. After the upgrade

  • remove compatibility shims added for the old version
  • update documentation and the project's minimum supported versions
  • note the next version's deprecations to address before the following upgrade
  • schedule the next upgrade rather than waiting for a forcing event

Patch releases

Patch upgrades (for example 5.2.4 to 5.2.6) are much simpler: they contain bug and security fixes with no intentional backward-incompatible changes. Treat them as routine dependency updates: update the pin, run CI, deploy. Security releases should go out the same day when the vulnerability affects features you use.

Verify

Verification check:

  • the target Django version runs in production with check --deploy passing
  • no RemovedInDjango*Warning warnings appear in staging logs
  • migrations applied cleanly with recorded duration
  • critical user flows tested manually in staging and spot-checked in production
  • error rate and latency match pre-upgrade baselines
  • dependencies are on versions that support the new Django

Explanation

Django's deprecation policy gives you a path: features are deprecated in one release, warned about for at least two, and removed later. Upgrading one feature release at a time, with warnings turned into errors, converts an upgrade into a series of small mechanical fixes. The remaining risk is behavioral rather than syntactic, which is why staging with real data and careful monitoring after the production deploy matter more than the version bump itself.

Edge cases / notes

Several major changes at once

Avoid upgrading Django, Python, the database, and infrastructure in one release. Sequence them so each change can be verified and rolled back independently.

Very old projects

Projects several years behind sometimes benefit from upgrading in a dedicated branch with intermediate deployments to staging, or from replacing unmaintained dependencies first.

Custom database backends or forks

Custom backends and patched Django forks need extra review, because internal APIs change between releases without deprecation cycles.

Areas that break most often

From experience across projects, these areas deserve extra attention during a Django upgrade:

AreaWhat changesHow to check
Settings defaultsnew defaults for storages, password hashers, form renderingdiff python manage.py diffsettings output before and after (filtering secrets)
Storages configurationthe STORAGES setting replaced older storage settingsconfirm static and media backends resolve as intended
Form and widget renderingtemplate-based rendering and CSS class changesvisual check of key forms, especially custom widgets
Admin templatesoverridden admin templates drift from upstreamcompare your overrides with the new version's originals
Timezone and date handlingstricter parsing or changed defaultstests around scheduling, reports, and filters
Third-party middlewarechanged middleware APIsrun the full request cycle in tests
Database backend featuresminimum database versions, new SQLcheck --database default, staging migrations
where upgrades usually breakTypical symptomThird-party apps!ImportError, pinned upper boundsSettingsremoved or renamed settingsCustom middlewareold-style middleware signaturesTemplate behaviourchanged escaping or renderingDatabase backends!minimum DB version raisedThird-party packages cause most upgrade delaysdjango-deployment.com
Check each dependency’s supported Django versions before starting.

Overridden Django templates are a frequent source of subtle breakage: they keep working but lose upstream fixes and new markup. Track which templates you override and review them at each upgrade.

Estimating effort

A rough guide for planning:

  • Patch release: minutes to an hour, including CI and deploy.
  • One feature release, well-maintained project: half a day to two days, most of it reading release notes and fixing deprecations.
  • Multiple releases behind, or unmaintained dependencies: a week or more, dominated by dependency replacement rather than Django itself.

The cheapest strategy is to keep up: each step stays in the first two categories.

Tracking supported versions

Add a recurring calendar reminder to check Django's supported versions page each quarter, recording:

  • the version you run and its end-of-life date
  • the next version you plan to move to, and when
  • Python versions supported by both

When the end-of-life date is within two quarters, schedule the upgrade work explicitly instead of leaving it to spare time.

Common mistakes

  • Skipping release notes and debugging surprises later.
  • Jumping several feature releases in one step.
  • Upgrading Django and Python together.
  • Deploying an upgrade without a rollback artifact ready.

Next steps

FAQ

How often should I upgrade Django?

Apply patch releases promptly, especially security releases. Move to a new feature or LTS release well before your current version reaches the end of its support window.

Can I upgrade Django several versions at once?

It is possible but harder. Upgrading one feature release at a time lets deprecation warnings guide the changes and keeps each step small enough to test.

How do I find code that will break in the next Django version?

Run the test suite with deprecation warnings as errors (python -W error::DeprecationWarning) on your current version, and read the release notes of the target version.

Should Django and Python be upgraded together?

Prefer separate releases. Each has its own compatibility surface, and separating them makes it clear which change caused a problem.

Keep going