Upgrading Django 3.2 to 5.2 LTS: a step-by-step plan
Django 3.2 stopped receiving security fixes in April 2024. Here is how we move apps to 5.2 LTS one release at a time, keeping production live throughout.
- Upgrade one feature release at a time: 3.2 → 4.0 → 4.1 → 4.2 → 5.0 → 5.1 → 5.2.
- Fix every deprecation warning before each step.
- Upgrade Python to 3.10 or later before you reach Django 5.0.
- Ship each step to production before starting the next.
Why upgrade now
Django 3.2 was a long-term support release, but its support window closed in April 2024. Since then it has received no security patches. Django 4.2 LTS followed it out of support in April 2026. Django 5.2 LTS is supported until April 2028, so it is the sensible target for most teams today.
Running an unsupported framework is a risk you carry silently until a vulnerability is published. Insurers, public-sector buyers and enterprise clients increasingly ask about it in security questionnaires.
The upgrade path
Jumping straight from 3.2 to 5.2 hides which change broke what. Step through each feature release instead. Each step is usually small.
| Step | Python | Watch for |
|---|---|---|
| 3.2 → 4.0 | 3.8+ | zoneinfo replaces pytz as default; CSRF_TRUSTED_ORIGINS needs a scheme |
| 4.0 → 4.1 | 3.8+ | Async ORM interfaces arrive; review custom form renderers |
| 4.1 → 4.2 | 3.8+ | STORAGES setting introduced; index_together deprecated |
| 4.2 → 5.0 | 3.10+ | Upgrade Python first; USE_L10N and pytz support removed |
| 5.0 → 5.1 | 3.10+ | Old storage settings and index_together removed |
| 5.1 → 5.2 | 3.10+ | Composite primary keys available; check third-party packages |
Before you start
Turn deprecation warnings into visible output, so every problem shows up in your test run rather than in production:
# show every deprecation warning
python -Wa manage.py test
# check third-party packages for supported versions
pip list --outdated
If test coverage is thin, write tests around the riskiest code first: authentication, payments, data migrations and anything that touches time zones.
Changes that usually bite
Storage settings moved into a single STORAGES dict in 4.2, and the old settings were removed in 5.1:
# before (removed in 5.1)
DEFAULT_FILE_STORAGE = "storages.backends.s3.S3Storage"
# after
STORAGES = {
"default": {"BACKEND": "storages.backends.s3.S3Storage"},
"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"},
}
Other common ones: CSRF_TRUSTED_ORIGINS needs a scheme from 4.0, pytz support was removed in 5.0 in favour of zoneinfo, and index_together must become Meta.indexes before 5.1.
Third-party packages are usually the slowest part. Check each one's supported Django versions before you plan the timeline.
Deploying each step
- Pin the next Django version and run the full test suite with warnings on.
- Fix failures and warnings, then run
makemigrations --check. - Deploy to staging and run smoke tests on real data.
- Ship to production and monitor errors and Core Web Vitals for a few days.
- Only then start the next step.
Questions
How long does a Django 3.2 to 5.2 upgrade take?
It depends on test coverage and third-party packages. Small apps can move in a few weeks; large monoliths take longer because each step is shipped separately.
Can we skip straight to Django 5.2?
You can, but every breaking change lands at once and is harder to diagnose. Stepping through each release is usually faster overall.
Do we need to upgrade Python too?
Yes. Django 5.0 and later need Python 3.10 or newer, so plan the Python upgrade before that step.
x