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, 3.13, and 3.14. 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_formattribute 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.hasmproperty checks whether the geometry has the M dimension.The new
Rotatedatabase function rotates a geometry by a specified angle around the origin or a specified point.The new
BaseGeometryWidget.base_layerattribute allows specifying a JavaScript map base layer, enabling customization of map tile providers.coveredbyandisvalidlookups,Collectaggregation, andGeoHashandIsValiddatabase functions are now supported on MariaDB 12.0.1+.The new
geom_typelookup andGeometryType()database function allow filtering geometries by their types.
django.contrib.postgres¶
The new
Lexemeexpression for full text search provides fine-grained control over search terms.Lexemeobjects automatically escape their input and support logical combination operators (&,|,~), prefix matching, and term weighting.Model fields, indexes, and constraints from
django.contrib.postgresnow include system checks to verify thatdjango.contrib.postgresis an installed app.The
CreateExtension,BloomExtension,BtreeGinExtension,BtreeGistExtension,CITextExtension,CryptoExtension,HStoreExtension,TrigramExtension, andUnaccentExtensionoperations now support the optionalhintsparameter. This allows providing database hints to database routers to assist them in making routing decisions.
django.contrib.staticfiles¶
ManifestStaticFilesStoragenow ensures consistent path ordering in manifest files, making them more reproducible and reducing unnecessary diffs.
Email¶
The new
policyargument 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 aMIMEPartobject from Python’s modern email API.
Internationalization¶
Added support and translations for the Haitian Creole language.
Management Commands¶
The
startprojectandstartappcommands now create the custom target directory if it doesn’t exist.Common utilities, such as
django.conf.settings, are now automatically imported to theshellby default.
Migrations¶
Squashed migrations can now themselves be squashed before being transitioned to normal migrations.
Migrations now support serialization of
zoneinfo.ZoneInfoinstances.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_byargument forAggregateallows specifying the ordering of the elements in the result.The new
Aggregate.allow_order_byclass attribute determines whether the aggregate function allows passing anorder_bykeyword argument.The new
StringAggaggregate returns the input values concatenated into a string, separated by thedelimiterstring. This aggregate was previously supported only for PostgreSQL.The
save()method now raises a specializedModel.NotUpdatedexception, 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
CompositePrimaryKeycan now be used as the target of lookups other than__in, such as__exact.JSONFieldnow supports negative array indexing on SQLite.The new
AnyValueaggregate returns an arbitrary value from the non-null input values. This is supported on SQLite, MySQL, Oracle, and PostgreSQL 16+.GeneratedFields and fields assigned expressions are now refreshed from the database aftersave()on backends that support theRETURNINGclause (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.Using a ForeignObject with multiple
from_fieldsin Model indexes, constraints, orunique_togethernow emits a system check error.
Pagination¶
The new
AsyncPaginatorandAsyncPageprovide async implementations ofPaginatorandPagerespectively.
Requests and Responses¶
Multiple
Cookieheaders are now supported for HTTP/2 requests when running with ASGI.
Templates¶
The new variable
forloop.lengthis now available within aforloop.The
querystringtemplate tag now consistently prefixes the returned query string with a?, ensuring reliable link generation behavior.The
querystringtemplate tag now accepts multiple positional arguments, which must be mappings, such asQueryDictordict.
Tests¶
The
DiscoverRunnernow supports parallel test execution on systems using theforkservermultiprocessingstart method.
Backwards incompatible changes in 6.0¶
Database backend API¶
This section describes changes that may be needed in third-party database backends.
BaseDatabaseSchemaEditorand PostgreSQL backends no longer useCASCADEwhen 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 … RETURNINGstatements as well asINSERT … RETURNING.The
DatabaseOperations.fetch_returned_insert_columns()method is removed and thefetch_returned_rows()method replacingfetch_returned_insert_rows()expects both acursorandreturning_paramsto be provided, just likefetch_returned_insert_columns()did.If the database supports
UPDATE … RETURNINGstatements, 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:
aiosmtpd1.4.5argon2-cffi23.1.0bcrypt4.1.1docutils0.22geoip24.8.0Pillow10.1.0mysqlclient2.2.1numpy1.26.0PyYAML6.0.2psycopg3.1.12psycopg22.9.9redis-py5.1.0selenium4.23.0sqlparse0.5.0tblib3.0.0
Email¶
The undocumented
mixed_subtypeandalternative_subtypeproperties ofEmailMessageandEmailMultiAlternativesare no longer supported.The undocumented
encodingproperty ofEmailMessageno longer supports Python legacyemail.charset.Charsetobjects.As the internal implementations of
EmailMessageandEmailMultiAlternativeshave 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
indentoption set.The undocumented
django.utils.http.parse_header_parameters()function is refactored to use Python’semail.message.Messagefor parsing. Input headers exceeding 10000 characters will now raiseValueError.Widgets from
django.contrib.gis.forms.widgetsnow 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.DEBUGandmessages.INFOnow have distinct icons and CSS styling in the admin. Previously, these used the same icon and styling as themessages.SUCCESSlevel. SinceModelAdmin.message_user()uses themessages.INFOlevel by default, set the level tomessages.SUCCESSto retain the previous icon and styling.The minimum supported version of
asgirefis increased from 3.8.1 to 3.9.1.The
collectstaticcommand now reports only a summary of skipped files due to conflicts when--verbosityis 1. To see warnings for each conflicting destination path, set the--verbosityflag to 2 or higher.The
collectstatic --clearcommand now reports only a summary of deleted files when--verbosityis 1. To see the details for each file deleted, set the--verbosityflag 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_silentlyand 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
EmailMessageorEmailMultiAlternativesinstance, except for the first four (subject,body,from_email, andto), which may still be passed either as positional or keyword arguments.
Miscellaneous¶
BaseDatabaseCreation.create_test_db(serialize)is deprecated. Useserialize_db_to_string()instead.The PostgreSQL
StringAggclass is deprecated in favor of the generally availableStringAggclass.The PostgreSQL
OrderableAggMixinis deprecated in favor of theorder_byattribute now available on theAggregateclass.The default protocol in
urlizeandurlizetruncwill change from HTTP to HTTPS in Django 7.0. Set the transitional settingURLIZE_ASSUME_HTTPStoTrueto opt into assuming HTTPS during the Django 6.x release cycle.URLIZE_ASSUME_HTTPStransitional setting is deprecated.Setting
ADMINSorMANAGERSto 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
orphansargument being larger than or equal to theper_pageargument ofdjango.core.paginator.Paginatoranddjango.core.paginator.AsyncPaginatoris deprecated.Using a percent sign in a column alias or annotation is deprecated.
Support for passing Python’s legacy email
MIMEBaseobject toEmailMessage.attach()(or including one in the message’sattachmentslist) is deprecated. For complex attachments requiring additional headers or parameters, switch to the modern email API’sMIMEPart.The
django.core.mail.BadHeaderErrorexception is deprecated. Python’s modern email raises aValueErrorfor email headers containing prohibited characters.The
django.core.mail.SafeMIMETextandSafeMIMEMultipartclasses 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
BaseConstraintis removed.The
DjangoDivFormRendererandJinja2DivFormRenderertransitional form renderers are removed.BaseDatabaseOperations.field_cast_sql()is removed.requestis 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.URLFieldhas changed from"http"to"https".The
FORMS_URLFIELD_ASSUME_HTTPStransitional setting is removed.The
django.db.models.sql.datastructures.Joinno longer falls back toget_joining_columns().The
get_joining_columns()method ofForeignObjectandForeignObjectRelis removed.The
ForeignObject.get_reverse_joining_columns()method is removed.Support for
cx_Oracleis removed.The
ChoicesMetaalias todjango.db.models.enums.ChoicesTypeis 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.itercompatmodule 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_dimis removed.The
checkkeyword argument ofCheckConstraintis removed.The
get_cache_name()method ofFieldCacheMixinis removed.The
OS_OPEN_FLAGSattribute ofFileSystemStorageis removed.