Expected December 2025
Welcome to Django 6.0!
These release notes cover the new features, as well as some backwards incompatible changes you’ll want to be aware of when upgrading from Django 5.2 or earlier. We’ve begun the deprecation process for some features.
See the How to upgrade Django to a newer version guide if you’re updating an existing project.
Django 6.0 supports Python 3.12 and 3.13. We highly recommend and only officially support the latest release of each series.
The Django 5.2.x series is the last to support Python 3.10 and 3.11.
Following the release of Django 6.0, we suggest that third-party app authors
drop support for all versions of Django prior to 5.2. At that time, you should
be able to run your package’s tests using python -Wd
so that deprecation
warnings appear. After making the deprecation warning fixes, your app should be
compatible with Django 6.0.
Built-in support for the Content Security Policy (CSP) standard is now available, making it easier to protect web applications against content injection attacks such as cross-site scripting (XSS). CSP allows declaring trusted sources of content by giving browsers strict rules about which scripts, styles, images, or other resources can be loaded.
CSP policies can now be enforced or monitored directly using built-in tools:
headers are added via the
ContentSecurityPolicyMiddleware
, nonces are
supported through the csp()
context
processor, and policies are configured using the SECURE_CSP
and
SECURE_CSP_REPORT_ONLY
settings.
These settings accept Python dictionaries and support Django-provided constants for clarity and safety. For example:
from django.utils.csp import CSP
SECURE_CSP = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE],
"img-src": [CSP.SELF, "https:"],
}
The resulting Content-Security-Policy
header would be set to:
default-src 'self'; script-src 'self' 'nonce-SECRET'; img-src 'self' https:
To get started, follow the CSP how-to guide. For in-depth guidance, see the CSP security overview and the reference docs.
Email handling in Django now uses Python’s modern email API, introduced in
Python 3.6. This API, centered around the
email.message.EmailMessage
class, offers a cleaner and
Unicode-friendly interface for composing and sending emails. It replaces use of
Python’s older legacy (Compat32
) API, which relied on lower-level MIME
classes (from email.mime
) and required more manual handling of
message structure and encoding.
Notably, the return type of the EmailMessage.message()
method is now an instance of Python’s
email.message.EmailMessage
. This supports the same API as the
previous SafeMIMEText
and SafeMIMEMultipart
return types, but is not an
instance of those now-deprecated classes.
django.contrib.admin
¶The Font Awesome Free icon set (version 6.7.2) is now used for the admin interface icons.
django.contrib.admindocs
¶The new AdminSite.password_change_form
attribute allows customizing
the form used in the admin site password change view.
django.contrib.auth
¶The default iteration count for the PBKDF2 password hasher is increased from 1,000,000 to 1,200,000.
django.contrib.contenttypes
¶…
django.contrib.gis
¶The new GEOSGeometry.hasm
property checks whether the geometry has
the M dimension.
The new Rotate
database
function rotates a geometry by a specified angle around the origin or a
specified point.
The new BaseGeometryWidget.base_layer
attribute allows specifying a
JavaScript map base layer, enabling customization of map tile providers.
coveredby
and isvalid
lookups,
Collect
aggregation, and
GeoHash
and
IsValid
database functions
are now supported on MariaDB 12.0.1+.
The new geom_type
lookup and
GeometryType()
database function allow filtering geometries by their types.
django.contrib.messages
¶…
django.contrib.postgres
¶Model fields, indexes, and constraints from django.contrib.postgres
now include system checks to verify that django.contrib.postgres
is an
installed app.
The CreateExtension
, BloomExtension
,
BtreeGinExtension
, BtreeGistExtension
,
CITextExtension
, CryptoExtension
,
HStoreExtension
, TrigramExtension
, and
UnaccentExtension
operations now support the optional hints
parameter. This allows providing database hints to database routers to assist
them in making routing decisions.
django.contrib.redirects
¶…
django.contrib.sessions
¶…
django.contrib.sitemaps
¶…
django.contrib.sites
¶…
django.contrib.staticfiles
¶ManifestStaticFilesStorage
now
ensures consistent path ordering in manifest files, making them more
reproducible and reducing unnecessary diffs.
…
…
…
The new policy
argument for EmailMessage.message()
allows specifying the email policy, the set
of rules for updating and serializing the representation of the message.
Defaults to email.policy.default
.
EmailMessage.attach()
now accepts a
MIMEPart
object from Python’s modern email API.
…
…
…
…
…
…
…
The startproject
and startapp
commands now create the
custom target directory if it doesn’t exist.
Common utilities, such as django.conf.settings
, are now automatically
imported to the shell
by default.
Squashed migrations can now themselves be squashed before being transitioned to normal migrations.
Migrations now support serialization of zoneinfo.ZoneInfo
instances.
Serialization of deconstructible objects now supports keyword arguments with names that are not valid Python identifiers.
Constraints now implement a check()
method that is already registered with the check framework.
The new order_by
argument for Aggregate
allows
specifying the ordering of the elements in the result.
The new Aggregate.allow_order_by
class attribute determines whether
the aggregate function allows passing an order_by
keyword argument.
The new StringAgg
aggregate returns the input
values concatenated into a string, separated by the delimiter
string.
This aggregate was previously supported only for PostgreSQL.
The save()
method now raises a specialized
Model.NotUpdated
exception, when
a forced update results in no affected rows,
instead of a generic django.db.DatabaseError
.
QuerySet.raw()
now supports models with a
CompositePrimaryKey
.
JSONField
now supports
negative array indexing on SQLite.
The new AnyValue
aggregate returns an arbitrary
value from the non-null input values. This is supported on SQLite, MySQL,
Oracle, and PostgreSQL 16+.
The new AsyncPaginator
and
AsyncPage
provide async implementations of
Paginator
and
Page
respectively.
…
…
…
…
The new variable forloop.length
is now available within a for
loop.
The querystring
template tag now consistently prefixes the returned
query string with a ?
, ensuring reliable link generation behavior.
The querystring
template tag now accepts multiple positional
arguments, which must be mappings, such as QueryDict
or dict
.
…
…
…
…
This section describes changes that may be needed in third-party database backends.
BaseDatabaseCreation.create_test_db(serialize)
is deprecated. Use
serialize_db_to_string()
instead.
BaseDatabaseSchemaEditor
and
PostgreSQL backends no longer use CASCADE
when dropping a column.
Upstream support for MariaDB 10.5 ends in June 2025. Django 6.0 supports MariaDB 10.6 and higher.
Because Python 3.12 is now the minimum supported version for Django, any optional dependencies must also meet that requirement. The following versions of each library are the first to add or confirm compatibility with Python 3.12:
aiosmtpd
1.4.5
argon2-cffi
23.1.0
bcrypt
4.1.1
geoip2
4.8.0
Pillow
10.1.0
mysqlclient
2.2.1
numpy
1.26.0
PyYAML
6.0.2
psycopg
3.1.12
psycopg2
2.9.9
redis-py
5.1.0
selenium
4.23.0
sqlparse
0.5.0
tblib
3.0.0
The undocumented mixed_subtype
and alternative_subtype
properties
of EmailMessage
and
EmailMultiAlternatives
are no longer supported.
The undocumented encoding
property of
EmailMessage
no longer supports Python legacy
email.charset.Charset
objects.
As the internal implementations of EmailMessage
and EmailMultiAlternatives
have changed
significantly, closely examine any custom subclasses that rely on overriding
undocumented, internal underscore methods.
The JSON serializer now writes a newline
at the end of the output, even without the indent
option set.
The undocumented django.utils.http.parse_header_parameters()
function is
refactored to use Python’s email.message.Message
for parsing.
Input headers exceeding 10000 characters will now raise ValueError
.
Widgets from django.contrib.gis.forms.widgets
now render without
inline JavaScript in templates. If you have customized any geometry widgets
or their templates, you may need to update them to match the new layout.
Message levels messages.DEBUG
and messages.INFO
now have distinct
icons and CSS styling in the admin. Previously, these used the same icon and
styling as the messages.SUCCESS
level. Since
ModelAdmin.message_user()
uses the messages.INFO
level by default,
set the level to messages.SUCCESS
to retain the previous icon and
styling.
The minimum supported version of asgiref
is increased from 3.8.1 to
3.9.1.
django.core.mail
APIs¶django.core.mail
APIs now require keyword arguments for less commonly
used parameters. Using positional arguments for these now emits a deprecation
warning and will raise a TypeError
when the deprecation period ends.
All optional parameters (fail_silently
and later) must be passed as
keyword arguments to get_connection()
, mail_admins()
,
mail_managers()
, send_mail()
, and send_mass_mail()
.
All parameters must be passed as keyword arguments when creating an
EmailMessage
or EmailMultiAlternatives
instance, except for
the first four (subject
, body
, from_email
, and to
), which may
still be passed either as positional or keyword arguments.
django.core.mail
APIs now require keyword arguments for less commonly
used parameters. Using positional arguments for these now emits a deprecation
warning and will raise a TypeError
when the deprecation period ends:
BaseDatabaseCreation.create_test_db(serialize)
is deprecated. Use
serialize_db_to_string()
instead.
The PostgreSQL StringAgg
class is deprecated in favor of the generally
available StringAgg
class.
The PostgreSQL OrderableAggMixin
is deprecated in favor of the
order_by
attribute now available on the Aggregate
class.
The default protocol in urlize
and urlizetrunc
will
change from HTTP to HTTPS in Django 7.0. Set the transitional setting
URLIZE_ASSUME_HTTPS
to True
to opt into assuming HTTPS during the
Django 6.x release cycle.
URLIZE_ASSUME_HTTPS
transitional setting is deprecated.
Setting ADMINS
or MANAGERS
to a list of (name, address)
tuples is deprecated. Set to a list of email address strings instead. Django
never used the name portion. To include a name, format the address string as
'"Name" <address>'
or use Python’s email.utils.formataddr()
.
Support for the orphans
argument being larger than or equal to the
per_page
argument of django.core.paginator.Paginator
and
django.core.paginator.AsyncPaginator
is deprecated.
Using a percent sign in a column alias or annotation is deprecated.
Support for passing Python’s legacy email MIMEBase
object to EmailMessage.attach()
(or
including one in the message’s attachments
list) is deprecated. For
complex attachments requiring additional headers or parameters, switch to the
modern email API’s MIMEPart
.
The django.core.mail.BadHeaderError
exception is deprecated. Python’s
modern email raises a ValueError
for email headers containing
prohibited characters.
The django.core.mail.SafeMIMEText
and SafeMIMEMultipart
classes are
deprecated.
The undocumented django.core.mail.forbid_multi_line_headers()
and
django.core.mail.message.sanitize_address()
functions are deprecated.
These features have reached the end of their deprecation cycle and are removed in Django 6.0.
See Features deprecated in 5.0 for details on these changes, including how to remove usage of these features.
Support for passing positional arguments to BaseConstraint
is removed.
The DjangoDivFormRenderer
and Jinja2DivFormRenderer
transitional form
renderers are removed.
BaseDatabaseOperations.field_cast_sql()
is removed.
request
is required in the signature of ModelAdmin.lookup_allowed()
subclasses.
Support for calling format_html()
without passing args or kwargs is
removed.
The default scheme for forms.URLField
changed from "http"
to
"https"
.
The FORMS_URLFIELD_ASSUME_HTTPS
transitional setting is removed.
The django.db.models.sql.datastructures.Join
no longer fallback to
get_joining_columns()
.
The get_joining_columns()
method of ForeignObject
and
ForeignObjectRel
is removed.
The ForeignObject.get_reverse_joining_columns()
method is removed.
Support for cx_Oracle
is removed.
The ChoicesMeta
alias to django.db.models.enums.ChoicesType
is
removed.
The Prefetch.get_current_queryset()
method is removed.
The get_prefetch_queryset()
method of related managers and descriptors is
removed.
get_prefetcher()
and prefetch_related_objects()
no longer fallback to
get_prefetch_queryset()
.
See Features deprecated in 5.1 for details on these changes, including how to remove usage of these features.
django.urls.register_converter()
no longer allows overriding existing
converters.
The ModelAdmin.log_deletion()
and LogEntryManager.log_action()
methods are removed.
The undocumented django.utils.itercompat.is_iterable()
function and the
django.utils.itercompat
module is removed.
The django.contrib.gis.geoip2.GeoIP2.coords()
method is removed.
The django.contrib.gis.geoip2.GeoIP2.open()
method is removed.
Support for passing positional arguments to Model.save()
and
Model.asave()
is removed.
The setter for django.contrib.gis.gdal.OGRGeometry.coord_dim
is removed.
The check
keyword argument of CheckConstraint
is removed.
The get_cache_name()
method of FieldCacheMixin
is removed.
The OS_OPEN_FLAGS
attribute of
FileSystemStorage
is removed.
Jul 26, 2025