Django 5.1 release notes - UNDER DEVELOPMENT

Expected August 2024

Welcome to Django 5.1!

These release notes cover the new features, as well as some backwards incompatible changes you should be aware of when upgrading from Django 5.0 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 5.1 supports Python 3.10, 3.11, and 3.12. We highly recommend and only officially support the latest release of each series.

What’s new in Django 5.1

{% query_string %} template tag

Django 5.1 introduces the {% query_string %} template tag, simplifying the modification of query parameters in URLs, making it easier to generate links that maintain existing query parameters while adding or changing specific ones.

For instance, navigating pagination and query strings in templates can be cumbersome. Consider this template fragment that dynamically generates a URL for navigating to the next page within a paginated view:

{# Linebreaks added for readability, this should be one, long line. #}
<a href="?{% for key, values in request.GET.iterlists %}
  {% if key != "page" %}
    {% for value in values %}
      {{ key }}={{ value }}&amp;
    {% endfor %}
  {% endif %}
{% endfor %}page={{ page.next_page_number }}">Next page</a>

When switching to using this new template tag, the above magically becomes:

<a href="{% query_string page=page.next_page_number %}">Next page</a>

PostgreSQL Connection Pools

Django 5.1 also introduces connection pool support for PostgreSQL. As the time to establish a new connection can be relatively long, keeping connections open can reduce latency.

To use a connection pool with psycopg, you can set the "pool" option inside OPTIONS to be a dict to be passed to ConnectionPool, or to True to use the ConnectionPool defaults:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        # ...
        "OPTIONS": {
            "pool": {
                "min_size": 2,
                "max_size": 4,
                "timeout": 10,
            }
        },
    },
}

Middleware to require authentication by default

The new LoginRequiredMiddleware redirects all unauthenticated requests to a login page. Views can allow unauthenticated requests by using the new login_not_required() decorator.

The LoginRequiredMiddleware respects the login_url and redirect_field_name values set via the login_required() decorator, but does not support setting login_url or redirect_field_name via the LoginRequiredMixin.

Minor features

django.contrib.admin

django.contrib.auth

  • The default iteration count for the PBKDF2 password hasher is increased from 720,000 to 870,000.

  • The default parallelism of the ScryptPasswordHasher is increased from 1 to 5, to follow OWASP recommendations.

  • BaseUserCreationForm and AdminPasswordChangeForm now support disabling password-based authentication by setting an unusable password on form save. This is now available in the admin when visiting the user creation and password change pages.

  • login_required(), permission_required(), and user_passes_test() decorators now support wrapping asynchronous view functions.

  • ReadOnlyPasswordHashWidget now includes a button to reset the user’s password, which replaces the link previously embedded in the ReadOnlyPasswordHashField’s help text, improving the overall accessibility of the UserChangeForm.

django.contrib.gis

django.contrib.postgres

  • BTreeIndex now supports the deduplicate_items parameter.

django.contrib.sessions

Database backends

Error Reporting

  • In order to improve accessibility, the technical 404 and 500 error pages now use HTML landmark elements for the header, footer, and main content areas.

File Storage

Forms

  • In order to improve accessibility and enable screen readers to associate fieldsets with their help text, the form fieldset now includes the aria-describedby HTML attribute.

Management Commands

Migrations

Models

Templates

  • Custom tags may now set extra data on the Parser object that will later be made available on the Template instance. Such data may be used, for example, by the template loader, or other template clients.

  • Template engines now implement a check() method that is already registered with the check framework.

Tests

  • assertContains(), assertNotContains(), and assertInHTML() assertions now add haystacks to assertion error messages.

  • The RequestFactory, AsyncRequestFactory, Client, and AsyncClient classes now support the query_params parameter, which accepts a dictionary of query string keys and values. This allows setting query strings on any HTTP methods more easily.

    self.client.post("/items/1", query_params={"action": "delete"})
    await self.async_client.post("/items/1", query_params={"action": "delete"})
    
  • The new SimpleTestCase.assertNotInHTML() assertion allows testing that an HTML fragment is not contained in the given HTML haystack.

  • In order to enforce test isolation, database connections inside threads are no longer allowed in SimpleTestCase.

Validators

Backwards incompatible changes in 5.1

django.contrib.gis

  • Support for PostGIS 2.5 is removed.

  • Support for PROJ < 6 is removed.

  • Support for GDAL 2.4 is removed.

  • GeoIP2 no longer opens both city and country databases when a directory path is provided, preferring the city database, if it is available. The country database is a subset of the city database and both are not typically needed. If you require use of the country database when in the same directory as the city database, explicitly pass the country database path to the constructor.

Dropped support for MariaDB 10.4

Upstream support for MariaDB 10.4 ends in June 2024. Django 5.1 supports MariaDB 10.5 and higher.

Dropped support for PostgreSQL 12

Upstream support for PostgreSQL 12 ends in November 2024. Django 5.1 supports PostgreSQL 13 and higher.

Miscellaneous

  • In order to improve accessibility, the admin’s changelist filter is now rendered in a <nav> tag instead of a <div>.

  • In order to improve accessibility, the admin’s footer is now rendered in a <footer> tag instead of a <div>, and also moved below the <div id="main"> element.

  • In order to improve accessibility, the expandable widget used for ModelAdmin.fieldsets and InlineModelAdmin.fieldsets, when the fieldset has a name and use the collapse class, now includes <details> and <summary> elements.

  • The JavaScript file collapse.js is removed since it is no longer needed in the Django admin site.

  • SimpleTestCase.assertURLEqual() and assertInHTML() now add ": " to the msg_prefix. This is consistent with the behavior of other assertions.

  • django.utils.text.Truncator used by truncatechars_html and truncatewords_html template filters now uses html.parser.HTMLParser subclasses. This results in a more robust and faster operation, but there may be small differences in the output.

  • The undocumented django.urls.converters.get_converter() function is removed.

  • The minimum supported version of SQLite is increased from 3.27.0 to 3.31.0.

  • FileField now raises a FieldError when saving a file without a name.

  • ImageField.update_dimension_fields(force=True) is no longer called after saving the image to storage. If your storage backend resizes images, the width_field and height_field will not match the width and height of the image.

Features deprecated in 5.1

Miscellaneous

  • The ModelAdmin.log_deletion() and LogEntryManager.log_action() methods are deprecated. Subclasses should implement ModelAdmin.log_deletions() and LogEntryManager.log_actions() instead.

  • The undocumented django.utils.itercompat.is_iterable() function and the django.utils.itercompat module are deprecated. Use isinstance(..., collections.abc.Iterable) instead.

  • The django.contrib.gis.geoip2.GeoIP2.coords() method is deprecated. Use django.contrib.gis.geoip2.GeoIP2.lon_lat() instead.

  • The django.contrib.gis.geoip2.GeoIP2.open() method is deprecated. Use the GeoIP2 constructor instead.

  • Passing positional arguments to Model.save() and Model.asave() is deprecated in favor of keyword-only arguments.

  • Setting django.contrib.gis.gdal.OGRGeometry.coord_dim is deprecated. Use set_3d() instead.

  • Overriding existing converters with django.urls.register_converter() is deprecated.

  • The check keyword argument of CheckConstraint is deprecated in favor of condition.

  • The undocumented OS_OPEN_FLAGS property of FileSystemStorage is deprecated. To allow overwriting files in storage, set the new allow_overwrite option to True instead.

  • The get_cache_name() method of FieldCacheMixin is deprecated in favor of the cache_name cached property.

Features removed in 5.1

These features have reached the end of their deprecation cycle and are removed in Django 5.1.

See Features deprecated in 4.2 for details on these changes, including how to remove usage of these features.

  • The BaseUserManager.make_random_password() method is removed.

  • The model’s Meta.index_together option is removed.

  • The length_is template filter is removed.

  • The django.contrib.auth.hashers.SHA1PasswordHasher, django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher, and django.contrib.auth.hashers.UnsaltedMD5PasswordHasher are removed.

  • The model django.contrib.postgres.fields.CICharField, django.contrib.postgres.fields.CIEmailField, and django.contrib.postgres.fields.CITextField are removed, except for support in historical migrations.

  • The django.contrib.postgres.fields.CIText mixin is removed.

  • The map_width and map_height attributes of BaseGeometryWidget are removed.

  • The SimpleTestCase.assertFormsetError() method is removed.

  • The TransactionTestCase.assertQuerysetEqual() method is removed.

  • Support for passing encoded JSON string literals to JSONField and associated lookups and expressions is removed.

  • Support for passing positional arguments to Signer and TimestampSigner is removed.

  • The DEFAULT_FILE_STORAGE and STATICFILES_STORAGE settings is removed.

  • The django.core.files.storage.get_storage_class() function is removed.