Django 6.0 release notes - UNDER DEVELOPMENT¶
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.
Python compatibility¶
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.
Third-party library support for older versions of Django¶
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.
What’s new in Django 6.0¶
Content Security Policy support¶
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, which include details about decorators to override or disable policies on a per-view basis.
Template Partials¶
The Django Template Language now supports
template partials, making it easier to encapsulate
and reuse small named fragments within a template file. The new tags
{% partialdef %}
and {% partial %}
define a partial and render it, respectively.
Partials can also be referenced using the template_name#partial_name
syntax
with get_template()
,
render()
, {% include %}
, and other
template-loading tools, enabling more modular and maintainable templates
without needing to split components into separate files.
A migration guide is available if you’re updating from the django-template-partials third-party package.
Background Tasks¶
Django now includes a built-in Tasks framework for running code outside the HTTP request–response cycle. This enables offloading work, such as sending emails or processing data, to background workers.
Tasks are defined using the task()
decorator:
from django.core.mail import send_mail
from django.tasks import task
@task
def email_users(emails, subject, message):
return send_mail(subject, message, None, emails)
Once defined, tasks can be enqueued through a configured backend:
email_users.enqueue(
emails=["user@example.com"],
subject="You have a message",
message="Hello there!",
)
Backends are configured via the TASKS
setting. The two
built-in backends included in this release are
primarily intended for development and testing.
Django handles task creation and queuing but does not provide a worker mechanism to run tasks. Execution must be managed by external infrastructure, such as a separate process or service.
See Django’s Tasks framework for an overview and the Tasks reference for API details.
Adoption of Python’s modern email API¶
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.
Minor features¶
django.contrib.admin
¶
The Font Awesome Free icon set (version 6.7.2) is now used for the admin interface icons.
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.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
andisvalid
lookups,Collect
aggregation, andGeoHash
andIsValid
database functions are now supported on MariaDB 12.0.1+.The new
geom_type
lookup andGeometryType()
database function allow filtering geometries by their types.
django.contrib.postgres
¶
The new
Lexeme
expression for full text search provides fine-grained control over search terms.Lexeme
objects automatically escape their input and support logical combination operators (&
,|
,~
), prefix matching, and term weighting.Model fields, indexes, and constraints from
django.contrib.postgres
now include system checks to verify thatdjango.contrib.postgres
is an installed app.The
CreateExtension
,BloomExtension
,BtreeGinExtension
,BtreeGistExtension
,CITextExtension
,CryptoExtension
,HStoreExtension
,TrigramExtension
, andUnaccentExtension
operations now support the optionalhints
parameter. This allows providing database hints to database routers to assist them in making routing decisions.
django.contrib.staticfiles
¶
ManifestStaticFilesStorage
now ensures consistent path ordering in manifest files, making them more reproducible and reducing unnecessary diffs.
Email¶
The new
policy
argument forEmailMessage.message()
allows specifying the email policy, the set of rules for updating and serializing the representation of the message. Defaults toemail.policy.default
.EmailMessage.attach()
now accepts aMIMEPart
object from Python’s modern email API.
Management Commands¶
The
startproject
andstartapp
commands now create the custom target directory if it doesn’t exist.Common utilities, such as
django.conf.settings
, are now automatically imported to theshell
by default.
Migrations¶
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.
Models¶
Constraints now implement a
check()
method that is already registered with the check framework.The new
order_by
argument forAggregate
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 anorder_by
keyword argument.The new
StringAgg
aggregate returns the input values concatenated into a string, separated by thedelimiter
string. This aggregate was previously supported only for PostgreSQL.The
save()
method now raises a specializedModel.NotUpdated
exception, when a forced update results in no affected rows, instead of a genericdjango.db.DatabaseError
.QuerySet.raw()
now supports models with aCompositePrimaryKey
.Subqueries returning a
CompositePrimaryKey
can now be used as the target of lookups other than__in
, such as__exact
.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+.GeneratedField
s and fields assigned expressions are now refreshed from the database aftersave()
on backends that support theRETURNING
clause (SQLite, PostgreSQL, and Oracle). On backends that don’t support it (MySQL and MariaDB), the fields are marked as deferred to trigger a refresh on subsequent accesses.
Pagination¶
The new
AsyncPaginator
andAsyncPage
provide async implementations ofPaginator
andPage
respectively.
Requests and Responses¶
Multiple
Cookie
headers are now supported for HTTP/2 requests when running with ASGI.
Templates¶
The new variable
forloop.length
is now available within afor
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 asQueryDict
ordict
.
Tests¶
The
DiscoverRunner
now supports parallel test execution on systems using theforkserver
multiprocessing
start method.
Backwards incompatible changes in 6.0¶
Database backend API¶
This section describes changes that may be needed in third-party database backends.
BaseDatabaseSchemaEditor
and PostgreSQL backends no longer useCASCADE
when dropping a column.DatabaseOperations.return_insert_columns()
andDatabaseOperations.fetch_returned_insert_rows()
methods are renamed toreturning_columns()
andfetch_returned_rows()
, respectively, to denote they can be used in the context ofUPDATE … RETURNING
statements as well asINSERT … RETURNING
.The
DatabaseOperations.fetch_returned_insert_columns()
method is removed and thefetch_returned_rows()
method replacingfetch_returned_insert_rows()
expects both acursor
andreturning_params
to be provided, just likefetch_returned_insert_columns()
did.If the database supports
UPDATE … RETURNING
statements, backends can setDatabaseFeatures.can_return_rows_from_update=True
.
Dropped support for MariaDB 10.5¶
Upstream support for MariaDB 10.5 ends in June 2025. Django 6.0 supports MariaDB 10.6 and higher.
Dropped support for Python < 3.12¶
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.5argon2-cffi
23.1.0bcrypt
4.1.1geoip2
4.8.0Pillow
10.1.0mysqlclient
2.2.1numpy
1.26.0PyYAML
6.0.2psycopg
3.1.12psycopg2
2.9.9redis-py
5.1.0selenium
4.23.0sqlparse
0.5.0tblib
3.0.0
Email¶
The undocumented
mixed_subtype
andalternative_subtype
properties ofEmailMessage
andEmailMultiAlternatives
are no longer supported.The undocumented
encoding
property ofEmailMessage
no longer supports Python legacyemail.charset.Charset
objects.As the internal implementations of
EmailMessage
andEmailMultiAlternatives
have changed significantly, closely examine any custom subclasses that rely on overriding undocumented, internal underscore methods.
DEFAULT_AUTO_FIELD
setting now defaults to BigAutoField
¶
Since Django 3.2, when the DEFAULT_AUTO_FIELD
setting was added,
the default startproject
template’s settings.py
contained:
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
and the default startapp
template’s AppConfig
contained:
default_auto_field = "django.db.models.BigAutoField"
At that time, the default value of DEFAULT_AUTO_FIELD
remained
django.db.models.AutoField
for backwards compatibility.
In Django 6.0, DEFAULT_AUTO_FIELD
now defaults to
django.db.models.BigAutoField
and the aforementioned lines in the
project and app templates are removed.
Most projects shouldn’t be affected, since Django 3.2 has raised the system
check warning models.W042 for projects that don’t set
DEFAULT_AUTO_FIELD
.
If you haven’t dealt with this warning by now, add
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
to your project’s
settings, or default_auto_field = 'django.db.models.AutoField'
to an app’s
AppConfig
, as needed.
Custom ORM expressions should return params as a tuple¶
Prior to Django 6.0, custom lookups and
custom expressions implementing the
as_sql()
method (and its supporting methods process_lhs()
and
process_rhs()
) were allowed to return a sequence of params in either a list
or a tuple. To address the interoperability problems that resulted, the second
return element of the as_sql()
method should now be a tuple:
def as_sql(self, compiler, connection) -> tuple[str, tuple]: ...
If your custom expressions support multiple versions of Django, you should adjust any pre-processing of parameters to be resilient against either tuples or lists. For instance, prefer unpacking like this:
params = (*lhs_params, *rhs_params)
Miscellaneous¶
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’semail.message.Message
for parsing. Input headers exceeding 10000 characters will now raiseValueError
.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
andmessages.INFO
now have distinct icons and CSS styling in the admin. Previously, these used the same icon and styling as themessages.SUCCESS
level. SinceModelAdmin.message_user()
uses themessages.INFO
level by default, set the level tomessages.SUCCESS
to retain the previous icon and styling.The minimum supported version of
asgiref
is increased from 3.8.1 to 3.9.1.The
collectstatic
command now reports only a summary of skipped files due to conflicts when--verbosity
is 1. To see warnings for each conflicting destination path, set the--verbosity
flag to 2 or higher.The
collectstatic --clear
command now reports only a summary of deleted files when--verbosity
is 1. To see the details for each file deleted, set the--verbosity
flag to 2 or higher.
Features deprecated in 6.0¶
Positional arguments in 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 toget_connection()
,mail_admins()
,mail_managers()
,send_mail()
, andsend_mass_mail()
.All parameters must be passed as keyword arguments when creating an
EmailMessage
orEmailMultiAlternatives
instance, except for the first four (subject
,body
,from_email
, andto
), 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 aTypeError
when the deprecation period ends:
Miscellaneous¶
BaseDatabaseCreation.create_test_db(serialize)
is deprecated. Useserialize_db_to_string()
instead.The PostgreSQL
StringAgg
class is deprecated in favor of the generally availableStringAgg
class.The PostgreSQL
OrderableAggMixin
is deprecated in favor of theorder_by
attribute now available on theAggregate
class.The default protocol in
urlize
andurlizetrunc
will change from HTTP to HTTPS in Django 7.0. Set the transitional settingURLIZE_ASSUME_HTTPS
toTrue
to opt into assuming HTTPS during the Django 6.x release cycle.URLIZE_ASSUME_HTTPS
transitional setting is deprecated.Setting
ADMINS
orMANAGERS
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’semail.utils.formataddr()
.Support for the
orphans
argument being larger than or equal to theper_page
argument ofdjango.core.paginator.Paginator
anddjango.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 toEmailMessage.attach()
(or including one in the message’sattachments
list) is deprecated. For complex attachments requiring additional headers or parameters, switch to the modern email API’sMIMEPart
.The
django.core.mail.BadHeaderError
exception is deprecated. Python’s modern email raises aValueError
for email headers containing prohibited characters.The
django.core.mail.SafeMIMEText
andSafeMIMEMultipart
classes are deprecated.The undocumented
django.core.mail.forbid_multi_line_headers()
anddjango.core.mail.message.sanitize_address()
functions are deprecated.
Features removed in 6.0¶
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
andJinja2DivFormRenderer
transitional form renderers are removed.BaseDatabaseOperations.field_cast_sql()
is removed.request
is required in the signature ofModelAdmin.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 falls back toget_joining_columns()
.The
get_joining_columns()
method ofForeignObject
andForeignObjectRel
is removed.The
ForeignObject.get_reverse_joining_columns()
method is removed.Support for
cx_Oracle
is removed.The
ChoicesMeta
alias todjango.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()
andprefetch_related_objects()
no longer fall back toget_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()
andLogEntryManager.log_action()
methods are removed.The undocumented
django.utils.itercompat.is_iterable()
function and thedjango.utils.itercompat
module are 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()
andModel.asave()
is removed.The setter for
django.contrib.gis.gdal.OGRGeometry.coord_dim
is removed.The
check
keyword argument ofCheckConstraint
is removed.The
get_cache_name()
method ofFieldCacheMixin
is removed.The
OS_OPEN_FLAGS
attribute ofFileSystemStorage
is removed.