{"title":"Django 4.0 release notes","version":"dev","locale":"en","docname":"releases/4.0","url":"/en/dev/releases/4.0/","canonical":"https://djangodocs.dev/en/dev/releases/4.0/","summary":"Welcome to Django 4.0! These release notes cover the new features, as well as some backwards incompatible changes you’ll want to be aware of when upgrading…","html":"<h1>Django 4.0 release notes<a class=\"heading-anchor\" href=\"#django-4-0-release-notes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p><em>December 7, 2021</em></p>\n<p>Welcome to Django 4.0!</p>\n<p>These release notes cover the <a class=\"reference internal\" href=\"#whats-new-4-0\"><span class=\"std std-ref\">new features</span></a>, as well as\nsome <a class=\"reference internal\" href=\"#backwards-incompatible-4-0\"><span class=\"std std-ref\">backwards incompatible changes</span></a> you’ll\nwant to be aware of when upgrading from Django 3.2 or earlier. We’ve\n<a class=\"reference internal\" href=\"#deprecated-features-4-0\"><span class=\"std std-ref\">begun the deprecation process for some features</span></a>.</p>\n<p>See the <a class=\"reference internal\" href=\"/en/dev/howto/upgrade-version/\"><span class=\"doc\">How to upgrade Django to a newer version</span></a> guide if you’re updating an existing\nproject.</p>\n<section id=\"python-compatibility\">\n<h2>Python compatibility<a class=\"heading-anchor\" href=\"#python-compatibility\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Django 4.0 supports Python 3.8, 3.9, and 3.10. We <strong>highly recommend</strong> and only\nofficially support the latest release of each series.</p>\n<p>The Django 3.2.x series is the last to support Python 3.6 and 3.7.</p>\n</section>\n<section id=\"what-s-new-in-django-4-0\">\n<span id=\"whats-new-4-0\"></span><h2>What’s new in Django 4.0<a class=\"heading-anchor\" href=\"#what-s-new-in-django-4-0\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"zoneinfo-default-timezone-implementation\">\n<h3><code class=\"docutils literal notranslate\">zoneinfo</code> default timezone implementation<a class=\"heading-anchor\" href=\"#zoneinfo-default-timezone-implementation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The Python standard library’s <a class=\"reference external\" href=\"https://docs.python.org/3/library/zoneinfo.html#module-zoneinfo\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">zoneinfo</code></a> is now the default timezone\nimplementation in Django.</p>\n<p>This is the next step in the migration from using <code class=\"docutils literal notranslate\">pytz</code> to using\n<a class=\"reference external\" href=\"https://docs.python.org/3/library/zoneinfo.html#module-zoneinfo\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">zoneinfo</code></a>. Django 3.2 allowed the use of non-<code class=\"docutils literal notranslate\">pytz</code> time zones. Django\n4.0 makes <code class=\"docutils literal notranslate\">zoneinfo</code> the default implementation. Support for <code class=\"docutils literal notranslate\">pytz</code> is now\ndeprecated and will be removed in Django 5.0.</p>\n<p><a class=\"reference external\" href=\"https://docs.python.org/3/library/zoneinfo.html#module-zoneinfo\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">zoneinfo</code></a> is part of the Python standard library from Python 3.9. The\n<code class=\"docutils literal notranslate\">backports.zoneinfo</code> package is automatically installed alongside Django if\nyou are using Python 3.8.</p>\n<p>The move to <code class=\"docutils literal notranslate\">zoneinfo</code> should be largely transparent. Selection of the\ncurrent timezone, conversion of datetime instances to the current timezone in\nforms and templates, as well as operations on aware datetimes in UTC are\nunaffected.</p>\n<p>However, if you are working with non-UTC time zones, and using the <code class=\"docutils literal notranslate\">pytz</code>\n<code class=\"docutils literal notranslate\">normalize()</code> and <code class=\"docutils literal notranslate\">localize()</code> APIs, possibly with the <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-DATABASE-TIME_ZONE\"><code class=\"xref std std-setting docutils literal notranslate\">TIME_ZONE</code></a> setting, you will need to audit your code, since <code class=\"docutils literal notranslate\">pytz</code>\nand <code class=\"docutils literal notranslate\">zoneinfo</code> are not entirely equivalent.</p>\n<p>To give time for such an audit, the transitional <code class=\"docutils literal notranslate\">USE_DEPRECATED_PYTZ</code>\nsetting allows continued use of <code class=\"docutils literal notranslate\">pytz</code> during the 4.x release cycle. This\nsetting will be removed in Django 5.0.</p>\n<p>In addition, a <a class=\"reference external\" href=\"https://pytz-deprecation-shim.readthedocs.io/en/latest/index.html\">pytz_deprecation_shim</a> package, created by the <code class=\"docutils literal notranslate\">zoneinfo</code>\nauthor, can be used to assist with the migration from <code class=\"docutils literal notranslate\">pytz</code>. This package\nprovides shims to help you safely remove <code class=\"docutils literal notranslate\">pytz</code>, and has a detailed\n<a class=\"reference external\" href=\"https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html\">migration guide</a> showing how to move to the new <code class=\"docutils literal notranslate\">zoneinfo</code> APIs.</p>\n<p>Using <a class=\"reference external\" href=\"https://pytz-deprecation-shim.readthedocs.io/en/latest/index.html\">pytz_deprecation_shim</a> and the <code class=\"docutils literal notranslate\">USE_DEPRECATED_PYTZ</code>\ntransitional setting is recommended if you need a gradual update path.</p>\n</section>\n<section id=\"functional-unique-constraints\">\n<h3>Functional unique constraints<a class=\"heading-anchor\" href=\"#functional-unique-constraints\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The new <a class=\"reference internal\" href=\"/en/dev/ref/models/constraints/#django.db.models.UniqueConstraint.expressions\" title=\"django.db.models.UniqueConstraint.expressions\"><code class=\"xref py py-attr docutils literal notranslate\">*expressions</code></a>\npositional argument of\n<a class=\"reference internal\" href=\"/en/dev/ref/models/constraints/#django.db.models.UniqueConstraint\" title=\"django.db.models.UniqueConstraint\"><code class=\"xref py py-class docutils literal notranslate\">UniqueConstraint()</code></a> enables\ncreating functional unique constraints on expressions and database functions.\nFor example:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> models\n<span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> UniqueConstraint\n<span class=\"kn\">from</span> <span class=\"nn\">django.db.models.functions</span> <span class=\"kn\">import</span> Lower\n\n\n<span class=\"k\">class</span> <span class=\"nc\">MyModel</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    first_name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">255</span><span class=\"p\">)</span>\n    last_name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">255</span><span class=\"p\">)</span>\n\n    <span class=\"k\">class</span> <span class=\"nc\">Meta</span><span class=\"p\">:</span>\n        constraints <span class=\"o\">=</span> <span class=\"p\">[</span>\n            UniqueConstraint<span class=\"p\">(</span>\n                Lower<span class=\"p\">(</span><span class=\"s2\">&quot;first_name&quot;</span><span class=\"p\">),</span>\n                Lower<span class=\"p\">(</span><span class=\"s2\">&quot;last_name&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>desc<span class=\"p\">(),</span>\n                name<span class=\"o\">=</span><span class=\"s2\">&quot;first_last_name_unique&quot;</span><span class=\"p\">,</span>\n            <span class=\"p\">),</span>\n        <span class=\"p\">]</span>\n</code></pre></div>\n<p>Functional unique constraints are added to models using the\n<a class=\"reference internal\" href=\"/en/dev/ref/models/options/#django.db.models.Options.constraints\" title=\"django.db.models.Options.constraints\"><code class=\"xref py py-attr docutils literal notranslate\">Meta.constraints</code></a> option.</p>\n</section>\n<section id=\"scrypt-password-hasher\">\n<h3><code class=\"docutils literal notranslate\">scrypt</code> password hasher<a class=\"heading-anchor\" href=\"#scrypt-password-hasher\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The new <a class=\"reference internal\" href=\"/en/dev/topics/auth/passwords/#scrypt-usage\"><span class=\"std std-ref\">scrypt password hasher</span></a> is more secure and\nrecommended over PBKDF2. However, it’s not the default as it requires OpenSSL\n1.1+ and more memory.</p>\n</section>\n<section id=\"redis-cache-backend\">\n<h3>Redis cache backend<a class=\"heading-anchor\" href=\"#redis-cache-backend\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The new <code class=\"docutils literal notranslate\">django.core.cache.backends.redis.RedisCache</code> cache backend provides\nbuilt-in support for caching with Redis. <a class=\"extlink-pypi reference external\" href=\"https://pypi.org/project/redis/\">redis-py</a> 3.0.0 or\nhigher is required. For more details, see the <a class=\"reference internal\" href=\"/en/dev/topics/cache/#redis\"><span class=\"std std-ref\">documentation on caching\nwith Redis in Django</span></a>.</p>\n</section>\n<section id=\"template-based-form-rendering\">\n<h3>Template based form rendering<a class=\"heading-anchor\" href=\"#template-based-form-rendering\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference internal\" href=\"/en/dev/ref/forms/api/#django.forms.Form\" title=\"django.forms.Form\"><code class=\"xref py py-class docutils literal notranslate\">Forms</code></a>, <a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/\"><span class=\"doc\">Formsets</span></a>,\nand <a class=\"reference internal\" href=\"/en/dev/ref/forms/api/#django.forms.ErrorList\" title=\"django.forms.ErrorList\"><code class=\"xref py py-class docutils literal notranslate\">ErrorList</code></a> are now rendered using the template engine\nto enhance customization. See the new <a class=\"reference internal\" href=\"/en/dev/ref/forms/api/#django.forms.Form.render\" title=\"django.forms.Form.render\"><code class=\"xref py py-meth docutils literal notranslate\">render()</code></a>,\n<a class=\"reference internal\" href=\"/en/dev/ref/forms/api/#django.forms.Form.get_context\" title=\"django.forms.Form.get_context\"><code class=\"xref py py-meth docutils literal notranslate\">get_context()</code></a>, and\n<a class=\"reference internal\" href=\"/en/dev/ref/forms/api/#django.forms.Form.template_name\" title=\"django.forms.Form.template_name\"><code class=\"xref py py-attr docutils literal notranslate\">template_name</code></a> for <code class=\"docutils literal notranslate\">Form</code> and\n<a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/#formset-rendering\"><span class=\"std std-ref\">formset rendering</span></a> for <code class=\"docutils literal notranslate\">Formset</code>.</p>\n</section>\n<section id=\"minor-features\">\n<h3>Minor features<a class=\"heading-anchor\" href=\"#minor-features\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<section id=\"django-contrib-admin\">\n<h4><a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#module-django.contrib.admin\" title=\"django.contrib.admin: Django's admin site.\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.admin</code></a><a class=\"heading-anchor\" href=\"#django-contrib-admin\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The <code class=\"docutils literal notranslate\">admin/base.html</code> template now has a new block <code class=\"docutils literal notranslate\">header</code> which\ncontains the admin site header.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_formset_kwargs\" title=\"django.contrib.admin.ModelAdmin.get_formset_kwargs\"><code class=\"xref py py-meth docutils literal notranslate\">ModelAdmin.get_formset_kwargs()</code></a> method allows customizing the\nkeyword arguments passed to the constructor of a formset.</p></li>\n<li><p>The navigation sidebar now has a quick filter toolbar.</p></li>\n<li><p>The new context variable <code class=\"docutils literal notranslate\">model</code> which contains the model class for each\nmodel is added to the <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.AdminSite.each_context\" title=\"django.contrib.admin.AdminSite.each_context\"><code class=\"xref py py-meth docutils literal notranslate\">AdminSite.each_context()</code></a> method.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_help_text\" title=\"django.contrib.admin.ModelAdmin.search_help_text\"><code class=\"xref py py-attr docutils literal notranslate\">ModelAdmin.search_help_text</code></a> attribute allows specifying a\ndescriptive text for the search box.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.verbose_name_plural\" title=\"django.contrib.admin.InlineModelAdmin.verbose_name_plural\"><code class=\"xref py py-attr docutils literal notranslate\">InlineModelAdmin.verbose_name_plural</code></a> attribute now fallbacks to\nthe <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.verbose_name\" title=\"django.contrib.admin.InlineModelAdmin.verbose_name\"><code class=\"xref py py-attr docutils literal notranslate\">InlineModelAdmin.verbose_name</code></a> + <code class=\"docutils literal notranslate\">'s'</code>.</p></li>\n<li><p>jQuery is upgraded from version 3.5.1 to 3.6.0.</p></li>\n</ul>\n</section>\n<section id=\"django-contrib-admindocs\">\n<h4><a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/admindocs/#module-django.contrib.admindocs\" title=\"django.contrib.admindocs: Django's admin documentation generator.\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.admindocs</code></a><a class=\"heading-anchor\" href=\"#django-contrib-admindocs\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The admindocs now allows esoteric setups where <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-ROOT_URLCONF\"><code class=\"xref std std-setting docutils literal notranslate\">ROOT_URLCONF</code></a> is not\na string.</p></li>\n<li><p>The model section of the <code class=\"docutils literal notranslate\">admindocs</code> now shows cached properties.</p></li>\n</ul>\n</section>\n<section id=\"django-contrib-auth\">\n<h4><a class=\"reference internal\" href=\"/en/dev/topics/auth/#module-django.contrib.auth\" title=\"django.contrib.auth: Django's authentication framework.\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.auth</code></a><a class=\"heading-anchor\" href=\"#django-contrib-auth\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The default iteration count for the PBKDF2 password hasher is increased from\n260,000 to 320,000.</p></li>\n<li><p>The new\n<a class=\"reference internal\" href=\"/en/dev/topics/auth/default/#django.contrib.auth.views.LoginView.next_page\" title=\"django.contrib.auth.views.LoginView.next_page\"><code class=\"xref py py-attr docutils literal notranslate\">LoginView.next_page</code></a>\nattribute and\n<a class=\"reference internal\" href=\"/en/dev/topics/auth/default/#django.contrib.auth.views.LoginView.get_default_redirect_url\" title=\"django.contrib.auth.views.LoginView.get_default_redirect_url\"><code class=\"xref py py-meth docutils literal notranslate\">get_default_redirect_url()</code></a> method\nallow customizing the redirect after login.</p></li>\n</ul>\n</section>\n<section id=\"django-contrib-gis\">\n<h4><a class=\"reference internal\" href=\"/en/dev/ref/contrib/gis/#module-django.contrib.gis\" title=\"django.contrib.gis: Geographic Information System (GIS) extensions for Django\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.gis</code></a><a class=\"heading-anchor\" href=\"#django-contrib-gis\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>Added support for SpatiaLite 5.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/contrib/gis/gdal/#django.contrib.gis.gdal.GDALRaster\" title=\"django.contrib.gis.gdal.GDALRaster\"><code class=\"xref py py-class docutils literal notranslate\">GDALRaster</code></a> now allows creating rasters in\nany GDAL virtual filesystem.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/contrib/gis/admin/#django.contrib.gis.admin.GISModelAdmin\" title=\"django.contrib.gis.admin.GISModelAdmin\"><code class=\"xref py py-class docutils literal notranslate\">GISModelAdmin</code></a> class allows\ncustomizing the widget used for <code class=\"docutils literal notranslate\">GeometryField</code>. This is encouraged instead\nof deprecated <code class=\"docutils literal notranslate\">GeoModelAdmin</code> and <code class=\"docutils literal notranslate\">OSMGeoAdmin</code>.</p></li>\n</ul>\n</section>\n<section id=\"django-contrib-postgres\">\n<h4><a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/#module-django.contrib.postgres\" title=\"django.contrib.postgres: PostgreSQL-specific fields and features\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.postgres</code></a><a class=\"heading-anchor\" href=\"#django-contrib-postgres\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The PostgreSQL backend now supports connecting by a service name. See\n<a class=\"reference internal\" href=\"/en/dev/ref/databases/#postgresql-connection-settings\"><span class=\"std std-ref\">PostgreSQL connection settings</span></a> for more details.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/operations/#django.contrib.postgres.operations.AddConstraintNotValid\" title=\"django.contrib.postgres.operations.AddConstraintNotValid\"><code class=\"xref py py-class docutils literal notranslate\">AddConstraintNotValid</code></a>\noperation allows creating check constraints on PostgreSQL without verifying\nthat all existing rows satisfy the new constraint.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/operations/#django.contrib.postgres.operations.ValidateConstraint\" title=\"django.contrib.postgres.operations.ValidateConstraint\"><code class=\"xref py py-class docutils literal notranslate\">ValidateConstraint</code></a>\noperation allows validating check constraints which were created using\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/operations/#django.contrib.postgres.operations.AddConstraintNotValid\" title=\"django.contrib.postgres.operations.AddConstraintNotValid\"><code class=\"xref py py-class docutils literal notranslate\">AddConstraintNotValid</code></a> on\nPostgreSQL.</p></li>\n<li><p>The new\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/expressions/#django.contrib.postgres.expressions.ArraySubquery\" title=\"django.contrib.postgres.expressions.ArraySubquery\"><code class=\"xref py py-class docutils literal notranslate\">ArraySubquery()</code></a>\nexpression allows using subqueries to construct lists of values on\nPostgreSQL.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/lookups/#std-fieldlookup-trigram_word_similar\"><code class=\"xref std std-lookup docutils literal notranslate\">trigram_word_similar</code></a> lookup, and the\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/search/#django.contrib.postgres.search.TrigramWordDistance\" title=\"django.contrib.postgres.search.TrigramWordDistance\"><code class=\"xref py py-class docutils literal notranslate\">TrigramWordDistance()</code></a> and\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/search/#django.contrib.postgres.search.TrigramWordSimilarity\" title=\"django.contrib.postgres.search.TrigramWordSimilarity\"><code class=\"xref py py-class docutils literal notranslate\">TrigramWordSimilarity()</code></a> expressions allow\nusing trigram word similarity.</p></li>\n</ul>\n</section>\n<section id=\"django-contrib-staticfiles\">\n<h4><a class=\"reference internal\" href=\"/en/dev/ref/contrib/staticfiles/#module-django.contrib.staticfiles\" title=\"django.contrib.staticfiles: An app for handling static files.\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.staticfiles</code></a><a class=\"heading-anchor\" href=\"#django-contrib-staticfiles\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage\" title=\"django.contrib.staticfiles.storage.ManifestStaticFilesStorage\"><code class=\"xref py py-class docutils literal notranslate\">ManifestStaticFilesStorage</code></a> now\nreplaces paths to JavaScript source map references with their hashed\ncounterparts.</p></li>\n<li><p>The new <code class=\"docutils literal notranslate\">manifest_storage</code> argument of\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestFilesMixin\" title=\"django.contrib.staticfiles.storage.ManifestFilesMixin\"><code class=\"xref py py-class docutils literal notranslate\">ManifestFilesMixin</code></a> and\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage\" title=\"django.contrib.staticfiles.storage.ManifestStaticFilesStorage\"><code class=\"xref py py-class docutils literal notranslate\">ManifestStaticFilesStorage</code></a>\nallows customizing the manifest file storage.</p></li>\n</ul>\n</section>\n<section id=\"cache\">\n<h4>Cache<a class=\"heading-anchor\" href=\"#cache\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul>\n<li><p>The new async API for <code class=\"docutils literal notranslate\">django.core.cache.backends.base.BaseCache</code> begins\nthe process of making cache backends async-compatible. The new async methods\nall have <code class=\"docutils literal notranslate\">a</code> prefixed names, e.g. <code class=\"docutils literal notranslate\">aadd()</code>, <code class=\"docutils literal notranslate\">aget()</code>, <code class=\"docutils literal notranslate\">aset()</code>,\n<code class=\"docutils literal notranslate\">aget_or_set()</code>, or <code class=\"docutils literal notranslate\">adelete_many()</code>.</p>\n<p>Going forward, the <code class=\"docutils literal notranslate\">a</code> prefix will be used for async variants of methods\ngenerally.</p>\n</li>\n</ul>\n</section>\n<section id=\"csrf\">\n<h4>CSRF<a class=\"heading-anchor\" href=\"#csrf\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>CSRF protection now consults the <code class=\"docutils literal notranslate\">Origin</code> header, if present. To facilitate\nthis, <a class=\"reference internal\" href=\"#csrf-trusted-origins-changes-4-0\"><span class=\"std std-ref\">some changes</span></a> to the\n<a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS\"><code class=\"xref std std-setting docutils literal notranslate\">CSRF_TRUSTED_ORIGINS</code></a> setting are required.</p></li>\n</ul>\n</section>\n<section id=\"forms\">\n<h4>Forms<a class=\"heading-anchor\" href=\"#forms\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/forms/fields/#django.forms.ModelChoiceField\" title=\"django.forms.ModelChoiceField\"><code class=\"xref py py-class docutils literal notranslate\">ModelChoiceField</code></a> now includes the provided value in\nthe <code class=\"docutils literal notranslate\">params</code> argument of a raised\n<a class=\"reference internal\" href=\"/en/dev/ref/exceptions/#django.core.exceptions.ValidationError\" title=\"django.core.exceptions.ValidationError\"><code class=\"xref py py-exc docutils literal notranslate\">ValidationError</code></a> for the <code class=\"docutils literal notranslate\">invalid_choice</code>\nerror message. This allows custom error messages to use the <code class=\"docutils literal notranslate\">%(value)s</code>\nplaceholder.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet\" title=\"django.forms.formsets.BaseFormSet\"><code class=\"xref py py-class docutils literal notranslate\">BaseFormSet</code></a> now renders non-form errors with\nan additional class of <code class=\"docutils literal notranslate\">nonform</code> to help distinguish them from\nform-specific errors.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet\" title=\"django.forms.formsets.BaseFormSet\"><code class=\"xref py py-class docutils literal notranslate\">BaseFormSet</code></a> now allows customizing the widget\nused when deleting forms via\n<a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet.can_delete\" title=\"django.forms.formsets.BaseFormSet.can_delete\"><code class=\"xref py py-attr docutils literal notranslate\">can_delete</code></a> by setting the\n<a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet.deletion_widget\" title=\"django.forms.formsets.BaseFormSet.deletion_widget\"><code class=\"xref py py-attr docutils literal notranslate\">deletion_widget</code></a> attribute or\noverriding <a class=\"reference internal\" href=\"/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet.get_deletion_widget\" title=\"django.forms.formsets.BaseFormSet.get_deletion_widget\"><code class=\"xref py py-meth docutils literal notranslate\">get_deletion_widget()</code></a>\nmethod.</p></li>\n</ul>\n</section>\n<section id=\"internationalization\">\n<h4>Internationalization<a class=\"heading-anchor\" href=\"#internationalization\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>Added support and translations for the Malay language.</p></li>\n</ul>\n</section>\n<section id=\"generic-views\">\n<h4>Generic Views<a class=\"heading-anchor\" href=\"#generic-views\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView\" title=\"django.views.generic.edit.DeleteView\"><code class=\"xref py py-class docutils literal notranslate\">DeleteView</code></a> now uses\n<a class=\"reference internal\" href=\"/en/dev/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin\" title=\"django.views.generic.edit.FormMixin\"><code class=\"xref py py-class docutils literal notranslate\">FormMixin</code></a>, allowing you to provide a\n<a class=\"reference internal\" href=\"/en/dev/ref/forms/api/#django.forms.Form\" title=\"django.forms.Form\"><code class=\"xref py py-class docutils literal notranslate\">Form</code></a> subclass, with a checkbox for example, to confirm\ndeletion. In addition, this allows <code class=\"docutils literal notranslate\">DeleteView</code> to function with\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/messages/#django.contrib.messages.views.SuccessMessageMixin\" title=\"django.contrib.messages.views.SuccessMessageMixin\"><code class=\"xref py py-class docutils literal notranslate\">django.contrib.messages.views.SuccessMessageMixin</code></a>.</p>\n<p>In accordance with <code class=\"docutils literal notranslate\">FormMixin</code>, object deletion for POST requests is\nhandled in <code class=\"docutils literal notranslate\">form_valid()</code>. Custom delete logic in <code class=\"docutils literal notranslate\">delete()</code> handlers\nshould be moved to <code class=\"docutils literal notranslate\">form_valid()</code>, or a shared helper method, as needed.</p>\n</li>\n</ul>\n</section>\n<section id=\"logging\">\n<h4>Logging<a class=\"heading-anchor\" href=\"#logging\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The alias of the database used in an SQL call is now passed as extra context\nalong with each message to the <a class=\"reference internal\" href=\"/en/dev/ref/logging/#django-db-logger\"><span class=\"std std-ref\">django.db.backends</span></a> logger.</p></li>\n</ul>\n</section>\n<section id=\"management-commands\">\n<h4>Management Commands<a class=\"heading-anchor\" href=\"#management-commands\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#django-admin-runserver\"><code class=\"xref std std-djadmin docutils literal notranslate\">runserver</code></a> management command now supports the\n<a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-skip-checks\"><code class=\"xref std std-option docutils literal notranslate\">--skip-checks</code></a> option.</p></li>\n<li><p>On PostgreSQL, <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#django-admin-dbshell\"><code class=\"xref std std-djadmin docutils literal notranslate\">dbshell</code></a> now supports specifying a password file.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\">shell</code></a> command now respects <a class=\"reference external\" href=\"https://docs.python.org/3/library/sys.html#sys.__interactivehook__\" title=\"(in Python v3.14)\"><code class=\"xref py py-data docutils literal notranslate\">sys.__interactivehook__</code></a>\nat startup. This allows loading shell history between interactive sessions.\nAs a consequence, <code class=\"docutils literal notranslate\">readline</code> is no longer loaded if running in <em>isolated</em>\nmode.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/howto/custom-management-commands/#django.core.management.BaseCommand.suppressed_base_arguments\" title=\"django.core.management.BaseCommand.suppressed_base_arguments\"><code class=\"xref py py-attr docutils literal notranslate\">BaseCommand.suppressed_base_arguments</code></a> attribute\nallows suppressing unsupported default command options in the help output.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-startapp-exclude\"><code class=\"xref std std-option docutils literal notranslate\">startapp --exclude</code></a> and <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-startproject-exclude\"><code class=\"xref std std-option docutils literal notranslate\">startproject --exclude</code></a>\noptions allow excluding directories from the template.</p></li>\n</ul>\n</section>\n<section id=\"models\">\n<h4>Models<a class=\"heading-anchor\" href=\"#models\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>New <a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.contains\" title=\"django.db.models.query.QuerySet.contains\"><code class=\"xref py py-meth docutils literal notranslate\">QuerySet.contains(obj)</code></a> method returns\nwhether the queryset contains the given object. This tries to perform the\nquery in the simplest and fastest way possible.</p></li>\n<li><p>The new <code class=\"docutils literal notranslate\">precision</code> argument of the\n<a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.Round\" title=\"django.db.models.functions.Round\"><code class=\"xref py py-class docutils literal notranslate\">Round()</code></a> database function allows\nspecifying the number of decimal places after rounding.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create\" title=\"django.db.models.query.QuerySet.bulk_create\"><code class=\"xref py py-meth docutils literal notranslate\">QuerySet.bulk_create()</code></a> now sets the primary key on objects when using\nSQLite 3.35+.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/fields/#django.db.models.DurationField\" title=\"django.db.models.DurationField\"><code class=\"xref py py-class docutils literal notranslate\">DurationField</code></a> now supports multiplying and\ndividing by scalar values on SQLite.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.bulk_update\" title=\"django.db.models.query.QuerySet.bulk_update\"><code class=\"xref py py-meth docutils literal notranslate\">QuerySet.bulk_update()</code></a> now returns the number of objects updated.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/models/expressions/#django.db.models.Expression.empty_result_set_value\" title=\"django.db.models.Expression.empty_result_set_value\"><code class=\"xref py py-attr docutils literal notranslate\">Expression.empty_result_set_value</code></a> attribute allows\nspecifying a value to return when the function is used over an empty result\nset.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">skip_locked</code> argument of <a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.select_for_update\" title=\"django.db.models.query.QuerySet.select_for_update\"><code class=\"xref py py-meth docutils literal notranslate\">QuerySet.select_for_update()</code></a> is now\nallowed on MariaDB 10.6+.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/lookups/#django.db.models.Lookup\" title=\"django.db.models.Lookup\"><code class=\"xref py py-class docutils literal notranslate\">Lookup</code></a> expressions may now be used in <code class=\"docutils literal notranslate\">QuerySet</code>\nannotations, aggregations, and directly in filters.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#aggregate-default\"><span class=\"std std-ref\">default</span></a> argument for built-in aggregates\nallows specifying a value to be returned when the queryset (or grouping)\ncontains no entries, rather than <code class=\"docutils literal notranslate\">None</code>.</p></li>\n</ul>\n</section>\n<section id=\"requests-and-responses\">\n<h4>Requests and Responses<a class=\"heading-anchor\" href=\"#requests-and-responses\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/middleware/#django.middleware.security.SecurityMiddleware\" title=\"django.middleware.security.SecurityMiddleware\"><code class=\"xref py py-class docutils literal notranslate\">SecurityMiddleware</code></a> now adds the\n<a class=\"reference internal\" href=\"/en/dev/ref/middleware/#cross-origin-opener-policy\"><span class=\"std std-ref\">Cross-Origin Opener Policy</span></a> header with a\nvalue of <code class=\"docutils literal notranslate\">'same-origin'</code> to prevent cross-origin popups from sharing the\nsame browsing context. You can prevent this header from being added by\nsetting the <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-SECURE_CROSS_ORIGIN_OPENER_POLICY\"><code class=\"xref std std-setting docutils literal notranslate\">SECURE_CROSS_ORIGIN_OPENER_POLICY</code></a> setting to <code class=\"docutils literal notranslate\">None</code>.</p></li>\n</ul>\n</section>\n<section id=\"signals\">\n<h4>Signals<a class=\"heading-anchor\" href=\"#signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The new <code class=\"docutils literal notranslate\">stdout</code> argument for <a class=\"reference internal\" href=\"/en/dev/ref/signals/#django.db.models.signals.pre_migrate\" title=\"django.db.models.signals.pre_migrate\"><code class=\"xref py py-func docutils literal notranslate\">pre_migrate()</code></a>\nand <a class=\"reference internal\" href=\"/en/dev/ref/signals/#django.db.models.signals.post_migrate\" title=\"django.db.models.signals.post_migrate\"><code class=\"xref py py-func docutils literal notranslate\">post_migrate()</code></a> signals allows redirecting\noutput to a stream-like object. It should be preferred over\n<a class=\"reference external\" href=\"https://docs.python.org/3/library/sys.html#sys.stdout\" title=\"(in Python v3.14)\"><code class=\"xref py py-data docutils literal notranslate\">sys.stdout</code></a> and <a class=\"reference external\" href=\"https://docs.python.org/3/library/functions.html#print\" title=\"(in Python v3.14)\"><code class=\"xref py py-func docutils literal notranslate\">print()</code></a> when emitting verbose output in\norder to allow proper capture when testing.</p></li>\n</ul>\n</section>\n<section id=\"templates\">\n<h4>Templates<a class=\"heading-anchor\" href=\"#templates\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/templates/builtins/#std-templatefilter-floatformat\"><code class=\"xref std std-tfilter docutils literal notranslate\">floatformat</code></a> template filter now allows using the <code class=\"docutils literal notranslate\">u</code> suffix to\nforce disabling localization.</p></li>\n</ul>\n</section>\n<section id=\"tests\">\n<h4>Tests<a class=\"heading-anchor\" href=\"#tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<ul class=\"simple\">\n<li><p>The new <code class=\"docutils literal notranslate\">serialized_aliases</code> argument of\n<a class=\"reference internal\" href=\"/en/dev/topics/testing/advanced/#django.test.utils.setup_databases\" title=\"django.test.utils.setup_databases\"><code class=\"xref py py-func docutils literal notranslate\">django.test.utils.setup_databases()</code></a> determines which\n<a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-DATABASES\"><code class=\"xref std std-setting docutils literal notranslate\">DATABASES</code></a> aliases test databases should have their state\nserialized to allow usage of the\n<a class=\"reference internal\" href=\"/en/dev/topics/testing/overview/#test-case-serialized-rollback\"><span class=\"std std-ref\">serialized_rollback</span></a> feature.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-test-buffer\"><code class=\"xref std std-option docutils literal notranslate\">test --buffer</code></a> option now supports parallel tests.</p></li>\n<li><p>The new <code class=\"docutils literal notranslate\">logger</code> argument to <a class=\"reference internal\" href=\"/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner\" title=\"django.test.runner.DiscoverRunner\"><code class=\"xref py py-class docutils literal notranslate\">DiscoverRunner</code></a>\nallows a Python <a class=\"reference external\" href=\"https://docs.python.org/3/library/logging.html#logger\" title=\"(in Python v3.14)\"><span class=\"xref std std-ref\">logger</span></a> to be used for logging.</p></li>\n<li><p>The new <a class=\"reference internal\" href=\"/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner.log\" title=\"django.test.runner.DiscoverRunner.log\"><code class=\"xref py py-meth docutils literal notranslate\">DiscoverRunner.log()</code></a> method provides a way to log messages\nthat uses the <code class=\"docutils literal notranslate\">DiscoverRunner.logger</code>, or prints to the console if not set.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner\" title=\"django.test.runner.DiscoverRunner\"><code class=\"xref py py-class docutils literal notranslate\">DiscoverRunner</code></a> can now execute tests in a random\norder using the <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-test-shuffle\"><code class=\"xref std std-option docutils literal notranslate\">test --shuffle</code></a> option.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-test-parallel\"><code class=\"xref std std-option docutils literal notranslate\">test --parallel</code></a> option now supports the value <code class=\"docutils literal notranslate\">auto</code> to run\none test process for each processor core.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/topics/testing/tools/#django.test.TestCase.captureOnCommitCallbacks\" title=\"django.test.TestCase.captureOnCommitCallbacks\"><code class=\"xref py py-meth docutils literal notranslate\">TestCase.captureOnCommitCallbacks()</code></a> now captures new callbacks added\nwhile executing <a class=\"reference internal\" href=\"/en/dev/topics/db/transactions/#django.db.transaction.on_commit\" title=\"django.db.transaction.on_commit\"><code class=\"xref py py-func docutils literal notranslate\">transaction.on_commit()</code></a> callbacks.</p></li>\n</ul>\n</section>\n</section>\n</section>\n<section id=\"backwards-incompatible-changes-in-4-0\">\n<span id=\"backwards-incompatible-4-0\"></span><h2>Backwards incompatible changes in 4.0<a class=\"heading-anchor\" href=\"#backwards-incompatible-changes-in-4-0\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"database-backend-api\">\n<h3>Database backend API<a class=\"heading-anchor\" href=\"#database-backend-api\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>This section describes changes that may be needed in third-party database\nbackends.</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">DatabaseOperations.year_lookup_bounds_for_date_field()</code> and\n<code class=\"docutils literal notranslate\">year_lookup_bounds_for_datetime_field()</code> methods now take the optional\n<code class=\"docutils literal notranslate\">iso_year</code> argument in order to support bounds for ISO-8601 week-numbering\nyears.</p></li>\n<li><p>The second argument of <code class=\"docutils literal notranslate\">DatabaseSchemaEditor._unique_sql()</code> and\n<code class=\"docutils literal notranslate\">_create_unique_sql()</code> methods is now <code class=\"docutils literal notranslate\">fields</code> instead of <code class=\"docutils literal notranslate\">columns</code>.</p></li>\n</ul>\n</section>\n<section id=\"id1\">\n<h3><a class=\"reference internal\" href=\"/en/dev/ref/contrib/gis/#module-django.contrib.gis\" title=\"django.contrib.gis: Geographic Information System (GIS) extensions for Django\"><code class=\"xref py py-mod docutils literal notranslate\">django.contrib.gis</code></a><a class=\"heading-anchor\" href=\"#id1\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<ul class=\"simple\">\n<li><p>Support for PostGIS 2.3 is removed.</p></li>\n<li><p>Support for GDAL 2.0 and GEOS 3.5 is removed.</p></li>\n</ul>\n</section>\n<section id=\"dropped-support-for-postgresql-9-6\">\n<h3>Dropped support for PostgreSQL 9.6<a class=\"heading-anchor\" href=\"#dropped-support-for-postgresql-9-6\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Upstream support for PostgreSQL 9.6 ends in November 2021. Django 4.0 supports\nPostgreSQL 10 and higher.</p>\n<p>Also, the minimum supported version of <code class=\"docutils literal notranslate\">psycopg2</code> is increased from 2.5.4 to\n2.8.4, as <code class=\"docutils literal notranslate\">psycopg2</code> 2.8.4 is the first release to support Python 3.8.</p>\n</section>\n<section id=\"dropped-support-for-oracle-12-2-and-18c\">\n<h3>Dropped support for Oracle 12.2 and 18c<a class=\"heading-anchor\" href=\"#dropped-support-for-oracle-12-2-and-18c\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Upstream support for Oracle 12.2 ends in March 2022 and for Oracle 18c it ends\nin June 2021. Django 3.2 will be supported until April 2024. Django 4.0\nofficially supports Oracle 19c.</p>\n</section>\n<section id=\"csrf-trusted-origins-changes\">\n<span id=\"csrf-trusted-origins-changes-4-0\"></span><h3><code class=\"docutils literal notranslate\">CSRF_TRUSTED_ORIGINS</code> changes<a class=\"heading-anchor\" href=\"#csrf-trusted-origins-changes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<section id=\"format-change\">\n<h4>Format change<a class=\"heading-anchor\" href=\"#format-change\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Values in the <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS\"><code class=\"xref std std-setting docutils literal notranslate\">CSRF_TRUSTED_ORIGINS</code></a> setting must include the scheme\n(e.g. <code class=\"docutils literal notranslate\">'http://'</code> or <code class=\"docutils literal notranslate\">'https://'</code>) instead of only the hostname.</p>\n<p>Also, values that started with a dot, must now also include an asterisk before\nthe dot. For example, change <code class=\"docutils literal notranslate\">'.example.com'</code> to <code class=\"docutils literal notranslate\">'https://*.example.com'</code>.</p>\n<p>A system check detects any required changes.</p>\n</section>\n<section id=\"configuring-it-may-now-be-required\">\n<h4>Configuring it may now be required<a class=\"heading-anchor\" href=\"#configuring-it-may-now-be-required\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>As CSRF protection now consults the <code class=\"docutils literal notranslate\">Origin</code> header, you may need to set\n<a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS\"><code class=\"xref std std-setting docutils literal notranslate\">CSRF_TRUSTED_ORIGINS</code></a>, particularly if you allow requests from\nsubdomains by setting <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-CSRF_COOKIE_DOMAIN\"><code class=\"xref std std-setting docutils literal notranslate\">CSRF_COOKIE_DOMAIN</code></a> (or\n<a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-SESSION_COOKIE_DOMAIN\"><code class=\"xref std std-setting docutils literal notranslate\">SESSION_COOKIE_DOMAIN</code></a> if <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-CSRF_USE_SESSIONS\"><code class=\"xref std std-setting docutils literal notranslate\">CSRF_USE_SESSIONS</code></a> is enabled) to\na value starting with a dot.</p>\n</section>\n</section>\n<section id=\"securitymiddleware-no-longer-sets-the-x-xss-protection-header\">\n<h3><code class=\"docutils literal notranslate\">SecurityMiddleware</code> no longer sets the <code class=\"docutils literal notranslate\">X-XSS-Protection</code> header<a class=\"heading-anchor\" href=\"#securitymiddleware-no-longer-sets-the-x-xss-protection-header\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The <a class=\"reference internal\" href=\"/en/dev/ref/middleware/#django.middleware.security.SecurityMiddleware\" title=\"django.middleware.security.SecurityMiddleware\"><code class=\"xref py py-class docutils literal notranslate\">SecurityMiddleware</code></a> no longer sets the\n<code class=\"docutils literal notranslate\">X-XSS-Protection</code> header if the <code class=\"docutils literal notranslate\">SECURE_BROWSER_XSS_FILTER</code> setting is\n<code class=\"docutils literal notranslate\">True</code>. The setting is removed.</p>\n<p>Most modern browsers don’t honor the <code class=\"docutils literal notranslate\">X-XSS-Protection</code> HTTP header. You can\nuse <a class=\"reference external\" href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy\">Content-Security-Policy</a> without allowing <code class=\"docutils literal notranslate\">'unsafe-inline'</code> scripts\ninstead.</p>\n<p>If you want to support legacy browsers and set the header, use this line in a\ncustom middleware:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>response<span class=\"o\">.</span>headers<span class=\"o\">.</span>setdefault<span class=\"p\">(</span><span class=\"s2\">&quot;X-XSS-Protection&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;1; mode=block&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"migrations-autodetector-changes\">\n<h3>Migrations autodetector changes<a class=\"heading-anchor\" href=\"#migrations-autodetector-changes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The migrations autodetector now uses model states instead of model classes.\nAlso, migration operations for <code class=\"docutils literal notranslate\">ForeignKey</code> and <code class=\"docutils literal notranslate\">ManyToManyField</code> fields no\nlonger specify attributes which were not passed to the fields during\ninitialization.</p>\n<p>As a side-effect, running <code class=\"docutils literal notranslate\">makemigrations</code> might generate no-op\n<code class=\"docutils literal notranslate\">AlterField</code> operations for <code class=\"docutils literal notranslate\">ManyToManyField</code> and <code class=\"docutils literal notranslate\">ForeignKey</code> fields in\nsome cases.</p>\n</section>\n<section id=\"deleteview-changes\">\n<h3><code class=\"docutils literal notranslate\">DeleteView</code> changes<a class=\"heading-anchor\" href=\"#deleteview-changes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference internal\" href=\"/en/dev/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView\" title=\"django.views.generic.edit.DeleteView\"><code class=\"xref py py-class docutils literal notranslate\">DeleteView</code></a> now uses\n<a class=\"reference internal\" href=\"/en/dev/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin\" title=\"django.views.generic.edit.FormMixin\"><code class=\"xref py py-class docutils literal notranslate\">FormMixin</code></a> to handle POST requests. As a\nconsequence, any custom deletion logic in <code class=\"docutils literal notranslate\">delete()</code> handlers should be\nmoved to <code class=\"docutils literal notranslate\">form_valid()</code>, or a shared helper method, if required.</p>\n</section>\n<section id=\"table-and-column-naming-scheme-changes-on-oracle\">\n<h3>Table and column naming scheme changes on Oracle<a class=\"heading-anchor\" href=\"#table-and-column-naming-scheme-changes-on-oracle\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Django 4.0 inadvertently changed the table and column naming scheme on Oracle.\nThis causes errors for models and fields with names longer than 30 characters.\nUnfortunately, renaming some Oracle tables and columns is required. Use the\nupgrade script in <a class=\"extlink-ticket reference external\" href=\"https://code.djangoproject.com/ticket/33789#comment:15\">33789</a> to generate <code class=\"docutils literal notranslate\">RENAME</code>\nstatements to change naming scheme.</p>\n</section>\n<section id=\"miscellaneous\">\n<h3>Miscellaneous<a class=\"heading-anchor\" href=\"#miscellaneous\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<ul class=\"simple\">\n<li><p>Support for <code class=\"docutils literal notranslate\">cx_Oracle</code> &lt; 7.0 is removed.</p></li>\n<li><p>To allow serving a Django site on a subpath without changing the value of\n<a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-STATIC_URL\"><code class=\"xref std std-setting docutils literal notranslate\">STATIC_URL</code></a>, the leading slash is removed from that setting (now\n<code class=\"docutils literal notranslate\">'static/'</code>) in the default <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#django-admin-startproject\"><code class=\"xref std std-djadmin docutils literal notranslate\">startproject</code></a> template.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.AdminSite\" title=\"django.contrib.admin.AdminSite\"><code class=\"xref py py-class docutils literal notranslate\">AdminSite</code></a> method for the admin <code class=\"docutils literal notranslate\">index</code>\nview is no longer decorated with <code class=\"docutils literal notranslate\">never_cache</code> when accessed directly,\nrather than via the recommended <code class=\"docutils literal notranslate\">AdminSite.urls</code> property, or\n<code class=\"docutils literal notranslate\">AdminSite.get_urls()</code> method.</p></li>\n<li><p>Unsupported operations on a sliced queryset now raise <code class=\"docutils literal notranslate\">TypeError</code> instead\nof <code class=\"docutils literal notranslate\">AssertionError</code>.</p></li>\n<li><p>The undocumented <code class=\"docutils literal notranslate\">django.test.runner.reorder_suite()</code> function is renamed\nto <code class=\"docutils literal notranslate\">reorder_tests()</code>. It now accepts an iterable of tests rather than a\ntest suite, and returns an iterator of tests.</p></li>\n<li><p>Calling <code class=\"docutils literal notranslate\">FileSystemStorage.delete()</code> with an empty <code class=\"docutils literal notranslate\">name</code> now raises\n<code class=\"docutils literal notranslate\">ValueError</code> instead of <code class=\"docutils literal notranslate\">AssertionError</code>.</p></li>\n<li><p>Calling <code class=\"docutils literal notranslate\">EmailMultiAlternatives.attach_alternative()</code> or\n<code class=\"docutils literal notranslate\">EmailMessage.attach()</code> with an invalid <code class=\"docutils literal notranslate\">content</code> or <code class=\"docutils literal notranslate\">mimetype</code>\narguments now raise <code class=\"docutils literal notranslate\">ValueError</code> instead of <code class=\"docutils literal notranslate\">AssertionError</code>.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/topics/testing/tools/#django.test.SimpleTestCase.assertHTMLEqual\" title=\"django.test.SimpleTestCase.assertHTMLEqual\"><code class=\"xref py py-meth docutils literal notranslate\">assertHTMLEqual()</code></a> no longer considers a\nnon-boolean attribute without a value equal to an attribute with the same\nname and value.</p></li>\n<li><p>Tests that fail to load, for example due to syntax errors, now always match\nwhen using <a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#cmdoption-test-tag\"><code class=\"xref std std-option docutils literal notranslate\">test --tag</code></a>.</p></li>\n<li><p>The undocumented <code class=\"docutils literal notranslate\">django.contrib.admin.utils.lookup_needs_distinct()</code>\nfunction is renamed to <code class=\"docutils literal notranslate\">lookup_spawns_duplicates()</code>.</p></li>\n<li><p>The undocumented <code class=\"docutils literal notranslate\">HttpRequest.get_raw_uri()</code> method is removed. The\n<a class=\"reference internal\" href=\"/en/dev/ref/request-response/#django.http.HttpRequest.build_absolute_uri\" title=\"django.http.HttpRequest.build_absolute_uri\"><code class=\"xref py py-meth docutils literal notranslate\">HttpRequest.build_absolute_uri()</code></a> method may be a suitable alternative.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">object</code> argument of undocumented <code class=\"docutils literal notranslate\">ModelAdmin.log_addition()</code>,\n<code class=\"docutils literal notranslate\">log_change()</code>, and <code class=\"docutils literal notranslate\">log_deletion()</code> methods is renamed to <code class=\"docutils literal notranslate\">obj</code>.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/utils/#django.utils.feedgenerator.RssFeed\" title=\"django.utils.feedgenerator.RssFeed\"><code class=\"xref py py-class docutils literal notranslate\">RssFeed</code></a>,\n<a class=\"reference internal\" href=\"/en/dev/ref/utils/#django.utils.feedgenerator.Atom1Feed\" title=\"django.utils.feedgenerator.Atom1Feed\"><code class=\"xref py py-class docutils literal notranslate\">Atom1Feed</code></a>, and their subclasses now emit\nelements with no content as self-closing tags.</p></li>\n<li><p><code class=\"docutils literal notranslate\">NodeList.render()</code> no longer casts the output of <code class=\"docutils literal notranslate\">render()</code> method for\nindividual nodes to a string. <code class=\"docutils literal notranslate\">Node.render()</code> should always return a string\nas documented.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">where_class</code> property of <code class=\"docutils literal notranslate\">django.db.models.sql.query.Query</code> and the\n<code class=\"docutils literal notranslate\">where_class</code> argument to the private <code class=\"docutils literal notranslate\">get_extra_restriction()</code> method of\n<code class=\"docutils literal notranslate\">ForeignObject</code> and <code class=\"docutils literal notranslate\">ForeignObjectRel</code> are removed. If needed, initialize\n<code class=\"docutils literal notranslate\">django.db.models.sql.where.WhereNode</code> instead.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">filter_clause</code> argument of the undocumented <code class=\"docutils literal notranslate\">Query.add_filter()</code>\nmethod is replaced by two positional arguments <code class=\"docutils literal notranslate\">filter_lhs</code> and\n<code class=\"docutils literal notranslate\">filter_rhs</code>.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/middleware/#django.middleware.csrf.CsrfViewMiddleware\" title=\"django.middleware.csrf.CsrfViewMiddleware\"><code class=\"xref py py-class docutils literal notranslate\">CsrfViewMiddleware</code></a> now uses\n<code class=\"docutils literal notranslate\">request.META['CSRF_COOKIE_NEEDS_UPDATE']</code> in place of\n<code class=\"docutils literal notranslate\">request.META['CSRF_COOKIE_USED']</code>, <code class=\"docutils literal notranslate\">request.csrf_cookie_needs_reset</code>,\nand <code class=\"docutils literal notranslate\">response.csrf_cookie_set</code> to track whether the CSRF cookie should be\nsent. This is an undocumented, private API.</p></li>\n<li><p>The undocumented <code class=\"docutils literal notranslate\">TRANSLATOR_COMMENT_MARK</code> constant is moved from\n<code class=\"docutils literal notranslate\">django.template.base</code> to <code class=\"docutils literal notranslate\">django.utils.translation.template</code>.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">real_apps</code> argument of the undocumented\n<code class=\"docutils literal notranslate\">django.db.migrations.state.ProjectState.__init__()</code> method must now be a\nset if provided.</p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/forms/widgets/#django.forms.RadioSelect\" title=\"django.forms.RadioSelect\"><code class=\"xref py py-class docutils literal notranslate\">RadioSelect</code></a> and\n<a class=\"reference internal\" href=\"/en/dev/ref/forms/widgets/#django.forms.CheckboxSelectMultiple\" title=\"django.forms.CheckboxSelectMultiple\"><code class=\"xref py py-class docutils literal notranslate\">CheckboxSelectMultiple</code></a> widgets are now rendered in\n<code class=\"docutils literal notranslate\">&lt;div&gt;</code> tags so they are announced more concisely by screen readers. If you\nneed the previous behavior, <a class=\"reference internal\" href=\"/en/dev/ref/forms/renderers/#overriding-built-in-widget-templates\"><span class=\"std std-ref\">override the widget template</span></a> with the appropriate template from\nDjango 3.2.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/templates/builtins/#std-templatefilter-floatformat\"><code class=\"xref std std-tfilter docutils literal notranslate\">floatformat</code></a> template filter no longer depends on the\n<code class=\"docutils literal notranslate\">USE_L10N</code> setting and always returns localized output. Use the <code class=\"docutils literal notranslate\">u</code>\nsuffix to disable localization.</p></li>\n<li><p>The default value of the <code class=\"docutils literal notranslate\">USE_L10N</code> setting is changed to <code class=\"docutils literal notranslate\">True</code>. See the\n<a class=\"reference internal\" href=\"#use-l10n-deprecation\"><span class=\"std std-ref\">Localization section</span></a> above for more details.</p></li>\n<li><p>As part of the <a class=\"reference internal\" href=\"#whats-new-4-0\"><span class=\"std std-ref\">move to zoneinfo</span></a>,\n<code class=\"docutils literal notranslate\">django.utils.timezone.utc</code> is changed to alias\n<a class=\"reference external\" href=\"https://docs.python.org/3/library/datetime.html#datetime.timezone.utc\" title=\"(in Python v3.14)\"><code class=\"xref py py-attr docutils literal notranslate\">datetime.timezone.utc</code></a>.</p></li>\n<li><p>The minimum supported version of <code class=\"docutils literal notranslate\">asgiref</code> is increased from 3.3.2 to\n3.4.1.</p></li>\n</ul>\n</section>\n</section>\n<section id=\"features-deprecated-in-4-0\">\n<span id=\"deprecated-features-4-0\"></span><h2>Features deprecated in 4.0<a class=\"heading-anchor\" href=\"#features-deprecated-in-4-0\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"use-of-pytz-time-zones\">\n<h3>Use of <code class=\"docutils literal notranslate\">pytz</code> time zones<a class=\"heading-anchor\" href=\"#use-of-pytz-time-zones\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>As part of the <a class=\"reference internal\" href=\"#whats-new-4-0\"><span class=\"std std-ref\">move to zoneinfo</span></a>, use of <code class=\"docutils literal notranslate\">pytz</code> time\nzones is deprecated.</p>\n<p>Accordingly, the <code class=\"docutils literal notranslate\">is_dst</code> arguments to the following are also deprecated:</p>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.datetimes\" title=\"django.db.models.query.QuerySet.datetimes\"><code class=\"xref py py-meth docutils literal notranslate\">django.db.models.query.QuerySet.datetimes()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.Trunc\" title=\"django.db.models.functions.Trunc\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.Trunc()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncSecond\" title=\"django.db.models.functions.TruncSecond\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncSecond()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncMinute\" title=\"django.db.models.functions.TruncMinute\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncMinute()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncHour\" title=\"django.db.models.functions.TruncHour\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncHour()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncDay\" title=\"django.db.models.functions.TruncDay\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncDay()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncWeek\" title=\"django.db.models.functions.TruncWeek\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncWeek()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncMonth\" title=\"django.db.models.functions.TruncMonth\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncMonth()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncQuarter\" title=\"django.db.models.functions.TruncQuarter\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncQuarter()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/models/database-functions/#django.db.models.functions.TruncYear\" title=\"django.db.models.functions.TruncYear\"><code class=\"xref py py-func docutils literal notranslate\">django.db.models.functions.TruncYear()</code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/en/dev/ref/utils/#django.utils.timezone.make_aware\" title=\"django.utils.timezone.make_aware\"><code class=\"xref py py-func docutils literal notranslate\">django.utils.timezone.make_aware()</code></a></p></li>\n</ul>\n<p>Support for use of <code class=\"docutils literal notranslate\">pytz</code> will be removed in Django 5.0.</p>\n</section>\n<section id=\"time-zone-support\">\n<h3>Time zone support<a class=\"heading-anchor\" href=\"#time-zone-support\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>In order to follow good practice, the default value of the <a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-USE_TZ\"><code class=\"xref std std-setting docutils literal notranslate\">USE_TZ</code></a>\nsetting will change from <code class=\"docutils literal notranslate\">False</code> to <code class=\"docutils literal notranslate\">True</code>, and time zone support will be\nenabled by default, in Django 5.0.</p>\n<p>Note that the default <code class=\"file docutils literal notranslate\">settings.py</code> file created by\n<a class=\"reference internal\" href=\"/en/dev/ref/django-admin/#django-admin-startproject\"><code class=\"xref std std-djadmin docutils literal notranslate\">django-admin startproject</code></a> includes\n<a class=\"reference internal\" href=\"/en/dev/ref/settings/#std-setting-USE_TZ\"><code class=\"xref std std-setting docutils literal notranslate\">USE_TZ = True</code></a> since Django 1.4.</p>\n<p>You can set <code class=\"docutils literal notranslate\">USE_TZ</code> to <code class=\"docutils literal notranslate\">False</code> in your project settings before then to\nopt-out.</p>\n</section>\n<section id=\"localization\">\n<span id=\"use-l10n-deprecation\"></span><h3>Localization<a class=\"heading-anchor\" href=\"#localization\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>In order to follow good practice, the default value of the <code class=\"docutils literal notranslate\">USE_L10N</code> setting\nis changed from <code class=\"docutils literal notranslate\">False</code> to <code class=\"docutils literal notranslate\">True</code>.</p>\n<p>Moreover <code class=\"docutils literal notranslate\">USE_L10N</code> is deprecated as of this release. Starting with Django\n5.0, by default, any date or number displayed by Django will be localized.</p>\n<p>The <a class=\"reference internal\" href=\"/en/dev/topics/i18n/formatting/#std-templatetag-localize\"><code class=\"xref std std-ttag docutils literal notranslate\">{% localize %}</code></a> tag and the <a class=\"reference internal\" href=\"/en/dev/topics/i18n/formatting/#std-templatefilter-localize\"><code class=\"xref std std-tfilter docutils literal notranslate\">localize</code></a>/\n<a class=\"reference internal\" href=\"/en/dev/topics/i18n/formatting/#std-templatefilter-unlocalize\"><code class=\"xref std std-tfilter docutils literal notranslate\">unlocalize</code></a> filters will still be honored by Django.</p>\n</section>\n<section id=\"id2\">\n<h3>Miscellaneous<a class=\"heading-anchor\" href=\"#id2\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">SERIALIZE</code> test setting is deprecated as it can be inferred from the\n<a class=\"reference internal\" href=\"/en/dev/topics/testing/tools/#django.test.TestCase.databases\" title=\"django.test.TestCase.databases\"><code class=\"xref py py-attr docutils literal notranslate\">databases</code></a> with the\n<a class=\"reference internal\" href=\"/en/dev/topics/testing/overview/#test-case-serialized-rollback\"><span class=\"std std-ref\">serialized_rollback</span></a> option enabled.</p></li>\n<li><p>The undocumented <code class=\"docutils literal notranslate\">django.utils.baseconv</code> module is deprecated.</p></li>\n<li><p>The undocumented <code class=\"docutils literal notranslate\">django.utils.datetime_safe</code> module is deprecated.</p></li>\n<li><p>The default sitemap protocol for sitemaps built outside the context of a\nrequest will change from <code class=\"docutils literal notranslate\">'http'</code> to <code class=\"docutils literal notranslate\">'https'</code> in Django 5.0.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">extra_tests</code> argument for <a class=\"reference internal\" href=\"/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner.build_suite\" title=\"django.test.runner.DiscoverRunner.build_suite\"><code class=\"xref py py-meth docutils literal notranslate\">DiscoverRunner.build_suite()</code></a> and\n<a class=\"reference internal\" href=\"/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner.run_tests\" title=\"django.test.runner.DiscoverRunner.run_tests\"><code class=\"xref py py-meth docutils literal notranslate\">DiscoverRunner.run_tests()</code></a> is deprecated.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/aggregates/#django.contrib.postgres.aggregates.ArrayAgg\" title=\"django.contrib.postgres.aggregates.ArrayAgg\"><code class=\"xref py py-class docutils literal notranslate\">ArrayAgg</code></a>,\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/aggregates/#django.contrib.postgres.aggregates.JSONBAgg\" title=\"django.contrib.postgres.aggregates.JSONBAgg\"><code class=\"xref py py-class docutils literal notranslate\">JSONBAgg</code></a>, and\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/postgres/aggregates/#django.contrib.postgres.aggregates.StringAgg\" title=\"django.contrib.postgres.aggregates.StringAgg\"><code class=\"xref py py-class docutils literal notranslate\">StringAgg</code></a> aggregates will return\n<code class=\"docutils literal notranslate\">None</code> when there are no rows instead of <code class=\"docutils literal notranslate\">[]</code>, <code class=\"docutils literal notranslate\">[]</code>, and <code class=\"docutils literal notranslate\">''</code>\nrespectively in Django 5.0. If you need the previous behavior, explicitly set\n<code class=\"docutils literal notranslate\">default</code> to <code class=\"docutils literal notranslate\">Value([])</code>, <code class=\"docutils literal notranslate\">Value('[]')</code>, or <code class=\"docutils literal notranslate\">Value('')</code>.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">django.contrib.gis.admin.GeoModelAdmin</code> and <code class=\"docutils literal notranslate\">OSMGeoAdmin</code> classes\nare deprecated. Use <a class=\"reference internal\" href=\"/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin\" title=\"django.contrib.admin.ModelAdmin\"><code class=\"xref py py-class docutils literal notranslate\">ModelAdmin</code></a> and\n<a class=\"reference internal\" href=\"/en/dev/ref/contrib/gis/admin/#django.contrib.gis.admin.GISModelAdmin\" title=\"django.contrib.gis.admin.GISModelAdmin\"><code class=\"xref py py-class docutils literal notranslate\">GISModelAdmin</code></a> instead.</p></li>\n<li><p>Since form rendering now uses the template engine, the undocumented\n<code class=\"docutils literal notranslate\">BaseForm._html_output()</code> helper method is deprecated.</p></li>\n<li><p>The ability to return a <code class=\"docutils literal notranslate\">str</code> from <code class=\"docutils literal notranslate\">ErrorList</code> and <code class=\"docutils literal notranslate\">ErrorDict</code> is\ndeprecated. It is expected these methods return a <code class=\"docutils literal notranslate\">SafeString</code>.</p></li>\n</ul>\n</section>\n</section>\n<section id=\"features-removed-in-4-0\">\n<h2>Features removed in 4.0<a class=\"heading-anchor\" href=\"#features-removed-in-4-0\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>These features have reached the end of their deprecation cycle and are removed\nin Django 4.0.</p>\n<p>See <a class=\"reference internal\" href=\"/en/dev/releases/3.0/#deprecated-features-3-0\"><span class=\"std std-ref\">Features deprecated in 3.0</span></a> for details on these changes, including how\nto remove usage of these features.</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\">django.utils.http.urlquote()</code>, <code class=\"docutils literal notranslate\">urlquote_plus()</code>, <code class=\"docutils literal notranslate\">urlunquote()</code>, and\n<code class=\"docutils literal notranslate\">urlunquote_plus()</code> are removed.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.utils.encoding.force_text()</code> and <code class=\"docutils literal notranslate\">smart_text()</code> are removed.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.utils.translation.ugettext()</code>, <code class=\"docutils literal notranslate\">ugettext_lazy()</code>,\n<code class=\"docutils literal notranslate\">ugettext_noop()</code>, <code class=\"docutils literal notranslate\">ungettext()</code>, and <code class=\"docutils literal notranslate\">ungettext_lazy()</code> are removed.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.views.i18n.set_language()</code> doesn’t set the user language in\n<code class=\"docutils literal notranslate\">request.session</code> (key <code class=\"docutils literal notranslate\">_language</code>).</p></li>\n<li><p><code class=\"docutils literal notranslate\">alias=None</code> is required in the signature of\n<code class=\"docutils literal notranslate\">django.db.models.Expression.get_group_by_cols()</code> subclasses.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.utils.text.unescape_entities()</code> is removed.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.utils.http.is_safe_url()</code> is removed.</p></li>\n</ul>\n<p>See <a class=\"reference internal\" href=\"/en/dev/releases/3.1/#deprecated-features-3-1\"><span class=\"std std-ref\">Features deprecated in 3.1</span></a> for details on these changes, including how\nto remove usage of these features.</p>\n<ul class=\"simple\">\n<li><p>The <code class=\"docutils literal notranslate\">PASSWORD_RESET_TIMEOUT_DAYS</code> setting is removed.</p></li>\n<li><p>The <a class=\"reference internal\" href=\"/en/dev/ref/models/querysets/#std-fieldlookup-isnull\"><code class=\"xref std std-lookup docutils literal notranslate\">isnull</code></a> lookup no longer allows using non-boolean values as the\nright-hand side.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">django.db.models.query_utils.InvalidQuery</code> exception class is removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">django-admin.py</code> entry point is removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">HttpRequest.is_ajax()</code> method is removed.</p></li>\n<li><p>Support for the pre-Django 3.1 encoding format of cookies values used by\n<code class=\"docutils literal notranslate\">django.contrib.messages.storage.cookie.CookieStorage</code> is removed.</p></li>\n<li><p>Support for the pre-Django 3.1 password reset tokens in the admin site (that\nuse the SHA-1 hashing algorithm) is removed.</p></li>\n<li><p>Support for the pre-Django 3.1 encoding format of sessions is removed.</p></li>\n<li><p>Support for the pre-Django 3.1 <code class=\"docutils literal notranslate\">django.core.signing.Signer</code> signatures\n(encoded with the SHA-1 algorithm) is removed.</p></li>\n<li><p>Support for the pre-Django 3.1 <code class=\"docutils literal notranslate\">django.core.signing.dumps()</code> signatures\n(encoded with the SHA-1 algorithm) in <code class=\"docutils literal notranslate\">django.core.signing.loads()</code> is\nremoved.</p></li>\n<li><p>Support for the pre-Django 3.1 user sessions (that use the SHA-1 algorithm)\nis removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">get_response</code> argument for\n<code class=\"docutils literal notranslate\">django.utils.deprecation.MiddlewareMixin.__init__()</code> is required and\ndoesn’t accept <code class=\"docutils literal notranslate\">None</code>.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">providing_args</code> argument for <code class=\"docutils literal notranslate\">django.dispatch.Signal</code> is removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">length</code> argument for <code class=\"docutils literal notranslate\">django.utils.crypto.get_random_string()</code> is\nrequired.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">list</code> message for <code class=\"docutils literal notranslate\">ModelMultipleChoiceField</code> is removed.</p></li>\n<li><p>Support for passing raw column aliases to <code class=\"docutils literal notranslate\">QuerySet.order_by()</code> is removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">NullBooleanField</code> model field is removed, except for support in\nhistorical migrations.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.conf.urls.url()</code> is removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">django.contrib.postgres.fields.JSONField</code> model field is removed,\nexcept for support in historical migrations.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.contrib.postgres.fields.jsonb.KeyTransform</code> and\n<code class=\"docutils literal notranslate\">django.contrib.postgres.fields.jsonb.KeyTextTransform</code> are removed.</p></li>\n<li><p><code class=\"docutils literal notranslate\">django.contrib.postgres.forms.JSONField</code> is removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">{% ifequal %}</code> and <code class=\"docutils literal notranslate\">{% ifnotequal %}</code> template tags are removed.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">DEFAULT_HASHING_ALGORITHM</code> transitional setting is removed.</p></li>\n</ul>\n</section>","rootId":"django-4-0-release-notes","toc":[{"title":"Python compatibility","anchor":"python-compatibility","children":[]},{"title":"What’s new in Django 4.0","anchor":"what-s-new-in-django-4-0","children":[{"title":"zoneinfo default timezone implementation","anchor":"zoneinfo-default-timezone-implementation","children":[]},{"title":"Functional unique constraints","anchor":"functional-unique-constraints","children":[]},{"title":"scrypt password hasher","anchor":"scrypt-password-hasher","children":[]},{"title":"Redis cache backend","anchor":"redis-cache-backend","children":[]},{"title":"Template based form rendering","anchor":"template-based-form-rendering","children":[]},{"title":"Minor features","anchor":"minor-features","children":[{"title":"django.contrib.admin","anchor":"django-contrib-admin","children":[]},{"title":"django.contrib.admindocs","anchor":"django-contrib-admindocs","children":[]},{"title":"django.contrib.auth","anchor":"django-contrib-auth","children":[]},{"title":"django.contrib.gis","anchor":"django-contrib-gis","children":[]},{"title":"django.contrib.postgres","anchor":"django-contrib-postgres","children":[]},{"title":"django.contrib.staticfiles","anchor":"django-contrib-staticfiles","children":[]},{"title":"Cache","anchor":"cache","children":[]},{"title":"CSRF","anchor":"csrf","children":[]},{"title":"Forms","anchor":"forms","children":[]},{"title":"Internationalization","anchor":"internationalization","children":[]},{"title":"Generic Views","anchor":"generic-views","children":[]},{"title":"Logging","anchor":"logging","children":[]},{"title":"Management Commands","anchor":"management-commands","children":[]},{"title":"Models","anchor":"models","children":[]},{"title":"Requests and Responses","anchor":"requests-and-responses","children":[]},{"title":"Signals","anchor":"signals","children":[]},{"title":"Templates","anchor":"templates","children":[]},{"title":"Tests","anchor":"tests","children":[]}]}]},{"title":"Backwards incompatible changes in 4.0","anchor":"backwards-incompatible-changes-in-4-0","children":[{"title":"Database backend API","anchor":"database-backend-api","children":[]},{"title":"django.contrib.gis","anchor":"id1","children":[]},{"title":"Dropped support for PostgreSQL 9.6","anchor":"dropped-support-for-postgresql-9-6","children":[]},{"title":"Dropped support for Oracle 12.2 and 18c","anchor":"dropped-support-for-oracle-12-2-and-18c","children":[]},{"title":"CSRF_TRUSTED_ORIGINS changes","anchor":"csrf-trusted-origins-changes","children":[{"title":"Format change","anchor":"format-change","children":[]},{"title":"Configuring it may now be required","anchor":"configuring-it-may-now-be-required","children":[]}]},{"title":"SecurityMiddleware no longer sets the X-XSS-Protection header","anchor":"securitymiddleware-no-longer-sets-the-x-xss-protection-header","children":[]},{"title":"Migrations autodetector changes","anchor":"migrations-autodetector-changes","children":[]},{"title":"DeleteView changes","anchor":"deleteview-changes","children":[]},{"title":"Table and column naming scheme changes on Oracle","anchor":"table-and-column-naming-scheme-changes-on-oracle","children":[]},{"title":"Miscellaneous","anchor":"miscellaneous","children":[]}]},{"title":"Features deprecated in 4.0","anchor":"features-deprecated-in-4-0","children":[{"title":"Use of pytz time zones","anchor":"use-of-pytz-time-zones","children":[]},{"title":"Time zone support","anchor":"time-zone-support","children":[]},{"title":"Localization","anchor":"localization","children":[]},{"title":"Miscellaneous","anchor":"id2","children":[]}]},{"title":"Features removed in 4.0","anchor":"features-removed-in-4-0","children":[]}],"breadcrumbs":[{"docname":"releases/index","title":"Release notes","url":"/en/dev/releases/"}],"prev":{"docname":"releases/4.0.1","title":"Django 4.0.1 release notes","url":"/en/dev/releases/4.0.1/"},"next":{"docname":"releases/3.2.25","title":"Django 3.2.25 release notes","url":"/en/dev/releases/3.2.25/"},"formats":{"html":"/en/dev/releases/4.0/","markdown":"/en/dev/releases/4.0.md","json":"/en/dev/releases/4.0.json"},"source":"https://github.com/django/django/blob/main/docs/releases/4.0.txt","official":"https://docs.djangoproject.com/en/dev/releases/4.0/","inVersions":["dev","6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0"],"inLocales":["en"]}