Key points
- The current version is Django 6.1.3, released on 3 November 2026. Django 4.2 LTS has had no security fixes since 7 April 2026.
- Django 5.2 LTS gets security fixes until April 2028 and is the last version for Python 3.10 and 3.11. Django 6 requires Python 3.12 or later.
- Django 6.0 added background tasks (
django.tasks), template partials, a built-in CSP and a modern email API. - Django 6.1 added field fetch modes that solve the N+1 query problem, database-level deletion of related rows and the
MAILERSsetting. It requires PostgreSQL 15 or later. - Django 6.2 LTS (April 2027) closes the current scheme. From January 2028 there will be one release a year (Django 2028, 2029 and so on), each with three years of support and no LTS label.
Django versions and support dates
Every Django version has two support periods. During mainstream support it gets bug fixes; during extended support it gets only security fixes and fixes for data-loss bugs. Every third version is an LTS with roughly three years of support.
| Version | Released | Python | Security fixes until |
|---|---|---|---|
| 4.2 LTS | April 2023 | 3.8 to 3.12 | 7 April 2026 (support ended) |
| 5.2 LTS | April 2025 | 3.10 to 3.14 | April 2028 |
| 6.0 | December 2025 | 3.12 to 3.14 | April 2027 |
| 6.1 | August 2026 | 3.12 to 3.15 | December 2027 |
| 6.2 LTS (planned) | April 2027 | 3.12 to 3.15 | April 2030 |
Dates from djangoproject.com/download and the Python version table in the Django documentation. Python 3.15 is supported from Django 6.1.3.
A new release cycle from 2028
Until now a new Django version came out roughly every 8 months. Django 6.2 will be the last version under this scheme. From 2028 versions will be released every January and named after the year: Django 2028, Django 2029 and so on. Each will get the same three-year support period, so the LTS label is no longer needed.
For a system owner this is simpler: a Django upgrade can be planned once a year at a fixed time, and there is no choice to make between an LTS and the latest version. The Django team recommends upgrading one version at a time.
Background tasks as standard
Sending emails, generating reports or importing files should not hold up the response to the user. Until now every project solved this its own way, most often with Celery. Django 6.0 adds a common API for defining and queuing tasks (django.tasks):
# settings.py (development)
TASKS = {"default": {"BACKEND": "django.tasks.backends.immediate.ImmediateBackend"}}
# tasks.py
from django.core.mail import send_mail
from django.tasks import task
@task(priority=2, queue_name="emails")
def email_users(emails, subject, message):
return send_mail(subject=subject, message=message, from_email=None, recipient_list=emails)
# in a view
result = email_users.enqueue(emails=["jane@example.com"], subject="Hello", message="...")
An important caveat: Django does not include a process that runs the tasks (a worker). The two built-in backends, ImmediateBackend and DummyBackend, are meant for development and testing only. Production needs a backend from a third-party library, with a worker and a durable queue. Existing solutions such as Celery keep working. The common API is most useful in new projects and in libraries that no longer have to assume a particular queue.
A task queued inside a transaction is best enqueued only after the transaction commits (transaction.on_commit), so the worker does not look for data that is not in the database yet.
Template partials and HTMX
Django 6.0 brought the popular django-template-partials library into core. A fragment defined with {% partialdef %} can be used many times in the same template or rendered on its own by giving its name after a #:
{% partialdef user-info inline %}
<div id="user-info-{{ user.username }}">
<h3>{{ user.name }}</h3>
</div>
{% endpartialdef %}
{% for user in featured %}
{% partial user-info %}
{% endfor %}
# the view returns just the fragment, for example for an HTMX request
return render(request, "authors.html#user-info", {"user": user})
This matters for applications that use HTMX instead of a large JavaScript frontend: the whole page and the parts that refresh live in one template file, instead of being split into dozens of small files. For many business and public-sector systems this approach is entirely sufficient. We cover it in more detail in our React or Angular comparison.
Built-in Content Security Policy
Content Security Policy is a header that tells the browser where a page may load scripts, styles and images from. Set up well, it limits the damage from XSS attacks: even if someone injects a script, the browser will not run it. A missing CSP header is a common finding in application penetration test reports. Until now Django needed a third-party library for this; from version 6.0 CSP support is built in:
# settings.py
from django.utils.csp import CSP
MIDDLEWARE = [
# ...
"django.middleware.csp.ContentSecurityPolicyMiddleware",
]
SECURE_CSP = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE],
"img-src": [CSP.SELF, "https:"],
}
<script nonce="{{ csp_nonce }}">...</script>
The one-time nonce reaches templates through the django.template.context_processors.csp context processor. Django 6.1 added the {% csp_nonce_attr %} tag, and admin templates now get the nonce automatically. It is best to switch the policy on in report-only mode first (SECURE_CSP_REPORT_ONLY) and enforce it only after checking what it would block. Django does not collect violation reports, so you need a separate endpoint or service for that.
The ORM in Django 6.1: fetch modes and database-level deletes
The most common performance problem in Django applications is the N+1 query problem: a loop over a list of records that fetches a related object separately for each one. With a hundred records that is a hundred and one database queries. Until now you had to remember select_related() or prefetch_related(). Django 6.1 introduces field fetch modes:
from django.db import models
books = Book.objects.fetch_mode(models.FETCH_PEERS)
for book in books:
print(book.author.name) # 2 queries instead of one per book
FETCH_ONEis the existing behaviour and still the default.FETCH_PEERSfetches a missing field for all records from the same query on first access, like an on-demandprefetch_related().FETCH_RAISEraisesFieldFetchBlockedwhen code tries to load more data. It is useful in tests to catch N+1 problems before they reach production.
The second addition is deletion of related rows by the database. The DB_CASCADE, DB_SET_NULL and DB_SET_DEFAULT options become an SQL ON DELETE clause, so Django does not have to load every related object into memory before deleting:
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.DB_CASCADE)
Limitations: with DB_CASCADE the pre_delete and post_delete signals do not fire for rows deleted this way, these options cannot be mixed with the regular ones (other than DO_NOTHING) in the same or related models, and DB_SET_DEFAULT needs db_default and does not work on MySQL or MariaDB.
Email and other changes
Email moved to the modern API of Python's standard library: EmailMessage.message() returns an email.message.EmailMessage object, and the old SafeMIMEText and SafeMIMEMultipart classes are deprecated. Optional arguments to the mail functions must be passed by keyword. Django 6.1 added the MAILERS setting, which lets you configure several mail servers, and the existing EMAIL_* settings are deprecated:
MAILERS = {
"default": {
"BACKEND": "django.core.mail.backends.smtp.EmailBackend",
"OPTIONS": {"host": "smtp.example.com", "use_tls": True},
},
}
Other changes worth knowing:
DEFAULT_AUTO_FIELDnow defaults toBigAutoField. Projects that relied on the implicitAutoFieldshould set it explicitly to avoid unwanted migrations.AsyncPaginatorandAsyncPagefor pagination in asynchronous views.- The
StringAggandAnyValueaggregates on every database backend, and in 6.1 alsoBitAnd,BitOrandBitXorplus theUUID4andUUID7functions. - More PBKDF2 iterations for password hashing: 1.2 million in 6.0 and 1.5 million in 6.1.
- In 6.1, accessibility improvements to admin forms.
What to watch for when upgrading
Django 6.0
- Requires Python 3.12 or later. Applications on Python 3.10 and 3.11 must change their Python version first.
- Support for MariaDB 10.5 and the
cx_Oracledriver is gone, and minimum versions of psycopg, Pillow and others went up. - Removed: positional arguments to
Model.save()and thecheckargument ofCheckConstraint(nowcondition).forms.URLFieldassumes https. - Custom
Field.pre_save()implementations may be called more than once.
Django 6.1
- Minimum database versions: PostgreSQL 15, MySQL 8.4, MariaDB 10.11, SQLite 3.37.
first()andlast()after an emptyorder_by()no longer order by primary key.- Aliases in generated SQL are always quoted, so raw queries that use mixed-case aliases may need fixes.
- Cache keys for pages and template fragments have changed. After deployment the cache fills up from scratch, which briefly increases load.
- Deprecated: the
EMAIL_*settings andselect_related()without arguments.
If the application uses PostgreSQL 14 or older, the database server must be upgraded before moving to Django 6.1. That is a separate project with a backup, testing and a maintenance window, so plan it well ahead.
Wagtail: which version for which Django
For sites built on Wagtail, the CMS decides the Django version. Wagtail releases a new version every 3 months and an LTS every few releases. Wagtail 7.0 LTS, the last version for Django 4.2, reached end of support on 2 November 2026.
| Wagtail | Django | Python | Supported until |
|---|---|---|---|
| 7.4 LTS | 5.2, 6.0 | 3.10 to 3.14 | 2 November 2027 |
| 8.1 | 5.2, 6.0, 6.1 | 3.11 to 3.15 | 4 May 2027 |
From the Wagtail release notes and release schedule. The next LTS is planned for May 2027.
For a site that should run steadily without frequent changes, we currently recommend Wagtail 7.4 LTS with Django 5.2 LTS. Django 6.1 requires Wagtail 8.0 or later, which are versions without long-term support that need upgrading every few months.
How to plan the upgrade
The Django documentation recommends upgrading step by step through each feature release, each time to its latest patch release. This also applies when moving from one LTS to the next. Deprecation warnings are fixed before changing version, because the deprecated features disappear in the next one.
- Starting pointThe versions of Django, Python, the database and every library. We check which libraries are abandoned and what can replace them.
- WarningsRunning tests with
python -Wa manage.py testshows every deprecated call. We fix them while still on the current version. - One version at a timeOne version per step, reading the release notes and running the full test suite after each. Dependent libraries are upgraded together with Django.
- Test environmentA copy of production data, migrations, background jobs, email sending, exports and integrations.
- DeploymentA database backup before migrations, a rollback plan and log monitoring after deployment. With the cache key change we expect a short spike in load.
- Applications on Django 4.2 and older moved to 5.2 LTS first.
- Python 3.12 or later before moving to Django 6.
- PostgreSQL 15 or later (or MySQL 8.4, MariaDB 10.11) before moving to Django 6.1.
- No deprecation warnings in the test run.
- An explicit
DEFAULT_AUTO_FIELDin settings. - For Wagtail sites, a CMS version that matches the target Django.
- A fixed date for the yearly upgrade in the maintenance plan, in line with the new release cycle.
Frequently asked questions
Does an application on Django 4.2 need upgrading?
Yes. Django 4.2 has had no security fixes since 7 April 2026, and 4.2.30 is its final release. The nearest sensible target is Django 5.2 LTS, supported until April 2028, which still runs on Python 3.10 and 3.11.
Should we stay on Django 5.2 LTS or move to 6.1?
Django 5.2 LTS is secure until April 2028, so there is no rush. If the application is actively developed, moving to 6.1 brings new features and puts the project into a yearly upgrade rhythm. If it is only maintained, Django 6.2 LTS from April 2027 is a sensible target.
Will django.tasks replace Celery?
Not on its own. Django defines a common task API but includes no worker, and the built-in backends are for development and testing only. Production needs a backend from a third-party library. Working Celery deployments need no changes.
What does the end of LTS versions from 2028 mean?
From 2028 every Django version will have the same three-year support period, so a separate LTS label is no longer needed. New versions will be released every January and named after the year, for example Django 2028.
How long does a Django upgrade take?
It depends on the age of the application and the quality of its tests. A small application with good tests moves to the next version in a few days. A large system that has not been upgraded for several years needs a few weeks, because abandoned libraries usually have to be replaced and the database upgraded as well.
Sources
- Django 6.0 release notes
- Django 6.1 release notes
- Django: supported versions and support dates
- Django: supported Python versions
- Django: upgrading to a newer version
- Django: background tasks (django.tasks)
- Wagtail 8.1 release notes
- Wagtail: release and support schedule
As of 6 November 2026. Dates of future Django and Wagtail versions are planned and may change.