• Language: en
  • 5.2
  • Documentation version: development

Django 5.2 release notes - UNDER DEVELOPMENT

Expected April 2025

Welcome to Django 5.2!

These release notes cover the new features, as well as some backwards incompatible changes you should be aware of when upgrading from Django 5.1 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 5.2 is designated as a long-term support release. It will receive security updates for at least three years after its release. Support for the previous LTS, Django 4.2, will end in April 2026.

Python compatibility

Django 5.2 supports Python 3.10, 3.11, 3.12, and 3.13. We highly recommend and only officially support the latest release of each series.

What’s new in Django 5.2

Automatic models import in the shell

The shell management command now automatically imports models from all installed apps. You can view further details of the imported objects by setting the --verbosity flag to 2 or more:

$ python -Wall manage.py shell --verbosity=2
6 objects imported automatically, including:

  from django.contrib.admin.models import LogEntry
  from django.contrib.auth.models import Group, Permission, User
  from django.contrib.contenttypes.models import ContentType
  from django.contrib.sessions.models import Session
...\> py -Wall manage.py shell --verbosity=2
6 objects imported automatically, including:

  from django.contrib.admin.models import LogEntry
  from django.contrib.auth.models import Group, Permission, User
  from django.contrib.contenttypes.models import ContentType
  from django.contrib.sessions.models import Session

This behavior can be customized to add or remove automatic imports.

Composite Primary Keys

The new django.db.models.CompositePrimaryKey allows tables to be created with a primary key consisting of multiple fields.

To use a composite primary key, when defining a model set the pk attribute to be a CompositePrimaryKey:

from django.db import models


class Release(models.Model):
    pk = models.CompositePrimaryKey("version", "name")
    version = models.IntegerField()
    name = models.CharField(max_length=20)

See Composite primary keys for more details.

Simplified override of BoundField

Prior to version 5.2, overriding Field.get_bound_field() was the only option to use a custom BoundField. Django now supports specifying the following attributes to customize form rendering:

For example, to customize the BoundField of a Form class:

from django import forms


class CustomBoundField(forms.BoundField):

    custom_class = "custom"

    def css_classes(self, extra_classes=None):
        result = super().css_classes(extra_classes)
        if self.custom_class not in result:
            result += f" {self.custom_class}"
        return result.strip()


class CustomForm(forms.Form):
    bound_field_class = CustomBoundField

    name = forms.CharField(
        label="Your Name",
        max_length=100,
        required=False,
        widget=forms.TextInput(attrs={"class": "name-input-class"}),
    )
    email = forms.EmailField(label="Your Email")

When rendering a CustomForm instance, the following HTML is included:

<div class="custom">
  <label for="id_name">Your Name:</label>
  <input type="text" name="name" class="name-input-class" maxlength="100" id="id_name">
</div>

<div class="custom">
  <label for="id_email">Your Email:</label>
  <input type="email" name="email" maxlength="320" required="" id="id_email">
</div>

See Customizing BoundField for more details about this feature.

Minor features

django.contrib.admin

  • The admin/base.html template now has a new block extrabody for adding custom code before the closing </body> tag.

  • The value of a URLField now renders as a link.

django.contrib.admindocs

  • Links to components in docstrings now supports custom link text, using the format :role:`link text <link>`. See documentation helpers for more details.

  • The model pages are now restricted to users with the corresponding view or change permissions.

django.contrib.auth

django.contrib.gis

django.contrib.syndication

  • All SyndicationFeed classes now support a stylesheets attribute. If specified, an <? xml-stylesheet ?> processing instruction will be added to the top of the document for each stylesheet in the given list. See Feed stylesheets for more details.

Database backends

  • MySQL connections now default to using the utf8mb4 character set, instead of utf8, which is an alias for the deprecated character set utf8mb3.

  • Oracle backends now support connection pools, by setting "pool" in the OPTIONS part of your database configuration.

Decorators

Email

Error Reporting

Forms

  • The new ColorInput form widget is for entering a color in rrggbb hexadecimal format and renders as <input type="color" ...>. Some browsers support a visual color picker interface for this input type.

  • The new SearchInput form widget is for entering search queries and renders as <input type="search" ...>.

  • The new TelInput form widget is for entering telephone numbers and renders as <input type="tel" ...>.

  • The new field_id argument for ErrorList allows an HTML id attribute to be added in the error template. See ErrorList.field_id for details.

  • An aria_describedby property is added to BoundField to ease use of this HTML attribute in templates.

  • To improve accessibility for screen reader users aria-describedby is used to associate form fields with their error messages. See how form errors are displayed for details.

  • The new asset object Script is available for adding custom HTML-attributes to JavaScript in form media. See paths as objects for more details.

Management Commands

  • A new warning is displayed when running runserver, indicating that it is unsuitable for production. This warning can be suppressed by setting the HIDE_PRODUCTION_WARNING environment variable to "true".

  • The makemigrations and migrate commands have a new Command.autodetector attribute for subclasses to override in order to use a custom autodetector class.

  • The new BaseCommand.get_check_kwargs() method can be overridden in custom commands to control the running of system checks, e.g. to opt into database-dependent checks.

Migrations

  • The new operation AlterConstraint is a no-op operation that alters constraints without dropping and recreating constraints in the database.

Models

  • The SELECT clause generated when using QuerySet.values() and QuerySet.values_list() now matches the specified order of the referenced expressions. Previously, the order was based on a set of counterintuitive rules which made query combination through methods such as QuerySet.union() unpredictable.

  • Added support for validation of model constraints which use a GeneratedField.

  • The new Expression.set_returning attribute specifies that the expression contains a set-returning function, enforcing subquery evaluation. This is necessary for many Postgres set-returning functions.

  • CharField.max_length is no longer required to be set on SQLite, which supports unlimited VARCHAR columns.

  • QuerySet.explain() now supports the memory and serialize options on PostgreSQL 17+.

  • The new JSONArray database function accepts a list of field names or expressions and returns a JSON array containing those values.

  • The new Expression.allows_composite_expressions attribute specifies that the expression allows composite expressions, for example, to support composite primary keys.

Requests and Responses

Serialization

  • Each serialization format now defines a Deserializer class, rather than a function, to improve extensibility when defining a custom serialization format.

Templates

  • The new simple_block_tag() decorator enables the creation of simple block tags, which can accept and use a section of the template.

Tests

  • Stack frames from Django’s custom assertions are now hidden. This makes test failures easier to read and enables test --pdb to directly enter into the failing test method.

  • Data loaded from fixtures and from migrations enabled with serialized_rollback=True are now available during TransactionTestCase.setUpClass().

URLs

  • reverse() now accepts query and fragment keyword arguments, allowing the addition of a query string and/or fragment identifier in the generated URL, respectively.

Utilities

  • SafeString now returns NotImplemented in __add__ for non-string right-hand side values. This aligns with the str addition behavior and allows __radd__ to be used if available.

  • format_html_join() now supports taking an iterable of mappings, passing their contents as keyword arguments to format_html().

Backwards incompatible changes in 5.2

Database backend API

This section describes changes that may be needed in third-party database backends.

  • The new Model._is_pk_set() method allows checking if a Model instance’s primary key is defined.

  • BaseDatabaseOperations.adapt_decimalfield_value() is now a no-op, simply returning the given value.

django.contrib.gis

  • Support for PostGIS 3.0 is removed.

  • Support for GDAL 3.0 is removed.

Dropped support for PostgreSQL 13

Upstream support for PostgreSQL 13 ends in November 2025. Django 5.2 supports PostgreSQL 14 and higher.

Changed MySQL connection character set default

MySQL connections now default to using the utf8mb4 character set, instead of utf8, which is an alias for the deprecated character set utf8mb3. utf8mb3 can be specified in the OPTIONS part of the DATABASES setting, if needed for legacy databases.

Miscellaneous

Features deprecated in 5.2

Miscellaneous

  • The all argument for the django.contrib.staticfiles.finders.find() function is deprecated in favor of the find_all argument.

  • The fallback to request.user when user is None in django.contrib.auth.login() and django.contrib.auth.alogin() will be removed.

  • The ordering keyword argument of the PostgreSQL specific aggregation functions django.contrib.postgres.aggregates.ArrayAgg, django.contrib.postgres.aggregates.JSONBAgg, and django.contrib.postgres.aggregates.StringAgg is deprecated in favor of the order_by argument.

Back to Top