============================================ Django 6.1 release notes - UNDER DEVELOPMENT ============================================ *Expected August 2026* Welcome to Django 6.1! These release notes cover the :ref:`new features `, as well as some :ref:`backwards incompatible changes ` you'll want to be aware of when upgrading from Django 6.0 or earlier. We've :ref:`begun the deprecation process for some features `. See the :doc:`/howto/upgrade-version` guide if you're updating an existing project. Python compatibility ==================== Django 6.1 supports Python 3.12, 3.13, and 3.14. We **highly recommend**, and only officially support, the latest release of each series. .. _whats-new-6.1: What's new in Django 6.1 ======================== Model field fetch modes ----------------------- The on-demand fetching behavior of model fields is now configurable with :doc:`fetch modes `. These modes allow you to control how Django fetches data from the database when an unfetched field is accessed. Django provides three fetch modes: 1. ``FETCH_ONE``, the default, fetches the missing field for the current instance only. This mode represents Django's existing behavior. 2. ``FETCH_PEERS`` fetches a missing field for all instances that came from the same :class:`~django.db.models.query.QuerySet`. This mode works like an on-demand ``prefetch_related()``. It can reduce most cases of the "N+1 queries problem" to two queries without any work to maintain a list of fields to prefetch. 3. ``RAISE`` raises a :exc:`~django.core.exceptions.FieldFetchBlocked` exception. This mode can prevent unintentional queries in performance-critical sections of code. Use the new method :meth:`.QuerySet.fetch_mode` to set the fetch mode for model instances fetched by the ``QuerySet``: .. code-block:: from django.db import models books = Book.objects.fetch_mode(models.FETCH_PEERS) for book in books: print(book.author.name) Despite the loop accessing the ``author`` foreign key on each instance, the ``FETCH_PEERS`` fetch mode will make the above example perform only two queries: 1. Fetch all books. 2. Fetch associated authors. See :doc:`fetch modes ` for more details. Database-level delete options for ``ForeignKey.on_delete`` ---------------------------------------------------------- :attr:`.ForeignKey.on_delete` now supports database-level delete options: * :attr:`~django.db.models.DB_CASCADE` * :attr:`~django.db.models.DB_SET_NULL` * :attr:`~django.db.models.DB_SET_DEFAULT` These options handle deletion logic entirely within the database, using the SQL ``ON DELETE`` clause. They are thus more efficient than the existing Python-level options, as Django does not need to load objects before deleting them. As a consequence, the :attr:`~django.db.models.DB_CASCADE` option does not trigger the ``pre_delete`` or ``post_delete`` signals. Mailers ------- The new :setting:`MAILERS` setting supports configuring multiple email backends with different options, similar to existing mechanisms for :setting:`CACHES`, :setting:`DATABASES`, :setting:`STORAGES`, and :setting:`TASKS`:: MAILERS = { "default": { "BACKEND": "django.core.mail.backends.smtp.EmailBackend", "OPTIONS": {"host": "smtp.example.com", "use_tls": True}, }, "marketing": { "BACKEND": "example.third.party.EmailBackend", "OPTIONS": {"region": "africa-1"}, }, } You can select a mailer with the new ``using`` argument to :ref:`email sending ` functions, or obtain an email backend instance with :data:`mail.mailers[alias] `. See :doc:`/topics/email` for more details. :setting:`MAILERS` is not yet enabled by default in existing projects. It will replace :setting:`EMAIL_BACKEND` and related ``EMAIL_*`` settings in Django 7.0. Until then, the older settings will continue to work but will issue deprecation warnings: see the list of :ref:`email deprecations ` below. You can opt into the new feature at any time before Django 7.0; see :ref:`migrating-to-mailers`. To ease the transition, :data:`mail.mailers["default"] ` works with either :setting:`MAILERS` or the deprecated :setting:`EMAIL_BACKEND` setting defined. The deprecated :func:`~django.core.mail.get_connection` function will also return an instance of the default mailer when :setting:`MAILERS` is defined. Minor features -------------- :mod:`django.contrib.admin` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ * The admin site login view now redirects authenticated users to the next URL, if available, instead of always redirecting to the admin index page. * The admin's ``FilteredSelectMultiple`` widget now uses ````\s to preserve :ref:`named groups ` (e.g. ``choices=[("Group", [("1", "Item")]), ...]``). * When :attr:`.ModelAdmin.list_select_related` is ``False`` (the default), the change list now selects only the foreign key fields specified in :attr:`.ModelAdmin.list_display`, rather than all foreign key fields. This should improve performance for models with many foreign key fields. * The :attr:`~django.contrib.admin.ModelAdmin.delete_confirmation_max_display` option allows customizing how many objects are displayed on admin delete confirmation pages and inline protected deletion errors before the remainder is truncated. The default is ``None`` (no truncation). * In order to improve accessibility of the admin change forms: * Form fields are now shown below their respective labels instead of next to them. * Help text is now shown after the field label and before the field input. * Validation errors are now shown after the help text and before the field input. * Checkboxes are an exception to the above changes and continue to be displayed in their original layout. * :attr:`~django.contrib.admin.ModelAdmin.list_display` now uses boolean icons for boolean fields on related models. * The new ``location`` keyword argument of the :func:`~django.contrib.admin.action` decorator specifies which admin views the action is available on. The action is available on the admin change list page by default. It can also be available on the admin change form. See :ref:`admin-action-availability` for details. * The new ``description_plural`` keyword argument of the :func:`~django.contrib.admin.action` decorator specifies a human-readable description for actions on the admin change list page. Defaults to the ``description`` value. This is useful when the action is available on both the admin change list and admin change form. :mod:`django.contrib.auth` ~~~~~~~~~~~~~~~~~~~~~~~~~~ * The default iteration count for the PBKDF2 password hasher is increased from 1,200,000 to 1,500,000. * :attr:`.Permission.name` and :attr:`.Permission.codename` values are now renamed when renaming models via a migration. * The new :attr:`.Permission.user_perm_str` property returns the string suitable to use with :meth:`.User.has_perm`. :mod:`django.contrib.gis` ~~~~~~~~~~~~~~~~~~~~~~~~~ * The :lookup:`isempty` lookup and :class:`IsEmpty() ` database function are now supported on SpatiaLite. * The new :lookup:`num_dimensions` lookup and :class:`NumDimensions() ` database function allow filtering geometries by the number of dimensions on PostGIS and SpatiaLite. * :class:`~django.contrib.gis.forms.widgets.OpenLayersWidget` is now based on OpenLayers 10.9.0 (previously 7.2.2). :mod:`django.contrib.postgres` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * :djadmin:`inspectdb` now introspects :class:`~django.contrib.postgres.fields.HStoreField` when ``psycopg`` 3.2+ is installed and ``django.contrib.postgres`` is in :setting:`INSTALLED_APPS`. * :class:`~django.contrib.postgres.constraints.ExclusionConstraint` now supports the Hash index type. :mod:`django.contrib.sessions` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * :class:`~django.contrib.sessions.backends.base.SessionBase` now supports boolean evaluation via :meth:`~django.contrib.sessions.backends.base.SessionBase.__bool__`. CSP ~~~ * The new :ttag:`csp_nonce_attr` template tag renders the CSP nonce attribute on ``