From be80d7aa9f5c0606ee16ae9593f0f4e607051473 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 09:50:56 +0100
Subject: [PATCH 01/25] [5.1.x] Post-release version bump.
---
django/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/django/__init__.py b/django/__init__.py
index 7bfb06dbce4c..fa2ee8caf2c0 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,6 +1,6 @@
from django.utils.version import get_version
-VERSION = (5, 1, 7, "final", 0)
+VERSION = (5, 1, 8, "alpha", 0)
__version__ = get_version(VERSION)
From 4b2ddd015ae20e1c11ab98e668e9198520322265 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 13:31:08 +0100
Subject: [PATCH 02/25] [5.1.x] Added stub release notes for 5.1.8.
Backport of 193e3446e38c5415465608f68620508eace60388 from main.
---
docs/releases/5.1.8.txt | 12 ++++++++++++
docs/releases/index.txt | 1 +
2 files changed, 13 insertions(+)
create mode 100644 docs/releases/5.1.8.txt
diff --git a/docs/releases/5.1.8.txt b/docs/releases/5.1.8.txt
new file mode 100644
index 000000000000..fd96acd0cb80
--- /dev/null
+++ b/docs/releases/5.1.8.txt
@@ -0,0 +1,12 @@
+==========================
+Django 5.1.8 release notes
+==========================
+
+*Expected April 2, 2025*
+
+Django 5.1.8 fixes several bugs in 5.1.7.
+
+Bugfixes
+========
+
+* ...
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 04c204c54de5..04f425ff6623 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 5.1.8
5.1.7
5.1.6
5.1.5
From 74d41970af2fa1b1d3955392096538c89afa4bf4 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 14:04:36 +0100
Subject: [PATCH 03/25] [5.1.x] Added CVE-2025-26699 to security archive.
Backport of bad1a18ff28a671f2fdfd447bdf8f43602f882c2 from main.
---
docs/releases/security.txt | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/releases/security.txt b/docs/releases/security.txt
index 0a87b8b81062..d55c7bf4971a 100644
--- a/docs/releases/security.txt
+++ b/docs/releases/security.txt
@@ -36,6 +36,17 @@ Issues under Django's security process
All security issues have been handled under versions of Django's security
process. These are listed below.
+March 6, 2025 - :cve:`2025-26699`
+---------------------------------
+
+Potential denial-of-service in ``django.utils.text.wrap()``.
+`Full description
+`__
+
+* Django 5.1 :commit:`(patch) <8dbb44d34271637099258391dfc79df33951b841>`
+* Django 5.0 :commit:`(patch) <4f2765232336b8ad0afd8017d9d912ae93470017>`
+* Django 4.2 :commit:`(patch) `
+
January 14, 2025 - :cve:`2024-56374`
------------------------------------
From ccd5867ae6115490a92dd51eead5c2b4e5c3b83a Mon Sep 17 00:00:00 2001
From: samruddhiDharankar
Date: Sat, 1 Mar 2025 14:53:20 -0800
Subject: [PATCH 04/25] [5.1.x] Fixed #36066 -- Documented that Q objects can
be used directly in annotations.
Backport of 9120a19c4ecb643111b073dd1069e6b410a03c23 from main.
---
AUTHORS | 1 +
docs/ref/models/querysets.txt | 12 ++++++++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index 02b9707cd468..2f7e3ece8b3d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -904,6 +904,7 @@ answer newbie questions, and generally made Django that much better:
Sachin Jat
Sage M. Abdullah
Sam Newman
+ Samruddhi Dharankar
Sander Dijkhuis
Sanket Saurav
Sanyam Khurana
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 9778140dd2f8..c6a4b82bcc82 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -258,10 +258,14 @@ you can use :class:`Q objects ` (``*args``).
.. method:: annotate(*args, **kwargs)
Annotates each object in the ``QuerySet`` with the provided list of :doc:`query
-expressions `. An expression may be a simple value, a
-reference to a field on the model (or any related models), or an aggregate
-expression (averages, sums, etc.) that has been computed over the objects that
-are related to the objects in the ``QuerySet``.
+expressions ` or :class:`~django.db.models.Q` objects.
+Each object can be annotated with:
+
+* a simple value, via ``Value()``;
+* a reference to a field on the model (or any related models), via ``F()``;
+* a boolean, via ``Q()``; or
+* a result from an aggregate expression (averages, sums, etc.) computed over
+ the objects that are related to the objects in the ``QuerySet``.
Each argument to ``annotate()`` is an annotation that will be added
to each object in the ``QuerySet`` that is returned.
From cfc33d146e1085909fe3c13b0fec8df955cd4272 Mon Sep 17 00:00:00 2001
From: Adam Johnson
Date: Wed, 12 Mar 2025 11:05:50 +0000
Subject: [PATCH 05/25] [5.1.x] Fixed #36234 -- Restored single_object argument
to LogEntry.objects.log_actions().
Thank you Adam Johnson for the report and fix. Thank you Sarah Boyce for
your spot on analysis.
Regression in c09bceef68e5abb79accedd12dade16aa6577a09, which is
partially reverted in this branch.
Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Backport of 27b68bcadf1ab2e9f7fd223aed42db352ccdc62d from main.
---
django/contrib/admin/models.py | 8 +++--
django/contrib/admin/options.py | 2 ++
docs/releases/5.1.8.txt | 4 ++-
tests/admin_utils/test_logentry.py | 49 +++++++++++++++++++++++++++++-
4 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py
index 345b8cf341aa..957c9e4877dc 100644
--- a/django/contrib/admin/models.py
+++ b/django/contrib/admin/models.py
@@ -51,7 +51,9 @@ def log_action(
change_message=change_message,
)
- def log_actions(self, user_id, queryset, action_flag, change_message=""):
+ def log_actions(
+ self, user_id, queryset, action_flag, change_message="", *, single_object=False
+ ):
# RemovedInDjango60Warning.
if type(self).log_action != LogEntryManager.log_action:
warnings.warn(
@@ -94,7 +96,9 @@ def log_actions(self, user_id, queryset, action_flag, change_message=""):
if len(log_entry_list) == 1:
instance = log_entry_list[0]
instance.save()
- return instance
+ if single_object:
+ return instance
+ return [instance]
return self.model.objects.bulk_create(log_entry_list)
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 4df5018804d6..5401bcabbebc 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -954,6 +954,7 @@ def log_addition(self, request, obj, message):
queryset=[obj],
action_flag=ADDITION,
change_message=message,
+ single_object=True,
)
def log_change(self, request, obj, message):
@@ -969,6 +970,7 @@ def log_change(self, request, obj, message):
queryset=[obj],
action_flag=CHANGE,
change_message=message,
+ single_object=True,
)
def log_deletion(self, request, obj, object_repr):
diff --git a/docs/releases/5.1.8.txt b/docs/releases/5.1.8.txt
index fd96acd0cb80..e5143904a260 100644
--- a/docs/releases/5.1.8.txt
+++ b/docs/releases/5.1.8.txt
@@ -9,4 +9,6 @@ Django 5.1.8 fixes several bugs in 5.1.7.
Bugfixes
========
-* ...
+* Fixed a regression in Django 5.1.7 where the removal of the ``single_object``
+ parameter unintentionally altered the signature and return type of
+ ``LogEntryManager.log_actions()`` (:ticket:`36234`).
diff --git a/tests/admin_utils/test_logentry.py b/tests/admin_utils/test_logentry.py
index 37ddb0da7d88..43b6cf55733a 100644
--- a/tests/admin_utils/test_logentry.py
+++ b/tests/admin_utils/test_logentry.py
@@ -271,12 +271,13 @@ def test_log_actions(self):
content_type = ContentType.objects.get_for_model(self.a1)
self.assertEqual(len(queryset), 3)
with self.assertNumQueries(1):
- LogEntry.objects.log_actions(
+ result = LogEntry.objects.log_actions(
self.user.pk,
queryset,
DELETION,
change_message=msg,
)
+ self.assertEqual(len(result), len(queryset))
logs = (
LogEntry.objects.filter(action_flag=DELETION)
.order_by("id")
@@ -300,6 +301,18 @@ def test_log_actions(self):
)
for obj in queryset
]
+ result_logs = [
+ (
+ entry.user_id,
+ entry.content_type_id,
+ str(entry.object_id),
+ entry.object_repr,
+ entry.action_flag,
+ entry.change_message,
+ )
+ for entry in result
+ ]
+ self.assertSequenceEqual(logs, result_logs)
self.assertSequenceEqual(logs, expected_log_values)
self.assertEqual(self.signals, [])
@@ -343,6 +356,40 @@ def test_log_action_fallback(self):
]
self.assertSequenceEqual(log_values, expected_log_values)
+ def test_log_actions_single_object_param(self):
+ queryset = Article.objects.filter(pk=self.a1.pk)
+ msg = "Deleted Something"
+ content_type = ContentType.objects.get_for_model(self.a1)
+ self.assertEqual(len(queryset), 1)
+ for single_object in (True, False):
+ self.signals = []
+ with self.subTest(single_object=single_object), self.assertNumQueries(1):
+ result = LogEntry.objects.log_actions(
+ self.user.pk,
+ queryset,
+ DELETION,
+ change_message=msg,
+ single_object=single_object,
+ )
+ if single_object:
+ self.assertIsInstance(result, LogEntry)
+ entry = result
+ else:
+ self.assertIsInstance(result, list)
+ self.assertEqual(len(result), 1)
+ entry = result[0]
+ self.assertEqual(entry.user_id, self.user.pk)
+ self.assertEqual(entry.content_type_id, content_type.id)
+ self.assertEqual(str(entry.object_id), str(self.a1.pk))
+ self.assertEqual(entry.object_repr, str(self.a1))
+ self.assertEqual(entry.action_flag, DELETION)
+ self.assertEqual(entry.change_message, msg)
+ expected_signals = [
+ ("pre_save", entry),
+ ("post_save", entry, True),
+ ]
+ self.assertEqual(self.signals, expected_signals)
+
def test_recentactions_without_content_type(self):
"""
If a LogEntry is missing content_type it will not display it in span
From d752ec8259a3a3123733cacbfb4f207ee61f6242 Mon Sep 17 00:00:00 2001
From: hesham hatem <80004117+Hesham942@users.noreply.github.com>
Date: Wed, 12 Mar 2025 23:09:04 +0200
Subject: [PATCH 06/25] [5.1.x] Fixed #36249 -- Fixed typo in
docs/topics/db/queries.txt.
Backport of e03440291b0599934da73b7dfbd2ccf7ec7270d8 from main.
---
docs/topics/db/queries.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 7e3338eaea33..2c2314198f83 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -1179,7 +1179,7 @@ To query for missing keys, use the ``isnull`` lookup:
... },
... )
- >>> Dogs.objects.annotate(
+ >>> Dog.objects.annotate(
... first_breed=KT("data__breed__1"), owner_name=KT("data__owner__name")
... ).filter(first_breed__startswith="lhasa", owner_name="Bob")
]>
From 67fc5805db8b4dead0f806a6715796ac32e9943b Mon Sep 17 00:00:00 2001
From: Clifford Gama
Date: Thu, 6 Mar 2025 23:29:21 +0200
Subject: [PATCH 07/25] [5.1.x] Corrected aggregation example in
docs/ref/models/querysets.txt.
Backport of 3235e76eb50be20756f82cb3bbe8e32cc586f7bb from main.
---
docs/ref/models/querysets.txt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index c6a4b82bcc82..e1234d107a4d 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2821,16 +2821,16 @@ number of authors that have contributed blog entries:
.. code-block:: pycon
>>> from django.db.models import Count
- >>> Blog.objects.aggregate(Count("entry"))
- {'entry__count': 16}
+ >>> Blog.objects.aggregate(Count("entry__authors"))
+ {'entry__authors__count': 16}
By using a keyword argument to specify the aggregate function, you can
control the name of the aggregation value that is returned:
.. code-block:: pycon
- >>> Blog.objects.aggregate(number_of_entries=Count("entry"))
- {'number_of_entries': 16}
+ >>> Blog.objects.aggregate(number_of_authors=Count("entry__authors"))
+ {'number_of_authors': 16}
For an in-depth discussion of aggregation, see :doc:`the topic guide on
Aggregation `.
From 8cb8820fbfbf2da4ebcae5f784cea3a0939d5639 Mon Sep 17 00:00:00 2001
From: Clifford Gama
Date: Fri, 7 Mar 2025 09:34:40 +0200
Subject: [PATCH 08/25] [5.1.x] Fixed pronoun disagreement in
docs/ref/models/querysets.txt.
Backport of ef6a83789b310a441237a190a493c9586a4cb260 from main.
---
docs/ref/models/querysets.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index e1234d107a4d..71af4c58820b 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2340,9 +2340,9 @@ whenever a request to a page has a side effect on your data. For more, see
# Raises IntegrityError
This is happening because it's trying to get or create "Chapter 1" through the
- book "Ulysses", but it can't do any of them: the relation can't fetch that
- chapter because it isn't related to that book, but it can't create it either
- because ``title`` field should be unique.
+ book "Ulysses", but it can't do either: the relation can't fetch that chapter
+ because it isn't related to that book, but it can't create it either because
+ ``title`` field should be unique.
``update_or_create()``
~~~~~~~~~~~~~~~~~~~~~~
From 71558701dfaf74e21160b76056ca7fc93cb2bd2f Mon Sep 17 00:00:00 2001
From: YQ
Date: Fri, 14 Mar 2025 17:20:34 +0800
Subject: [PATCH 09/25] [5.1.x] Fixed #36254 -- Fixed template dictionary
unpacking in docs/topics/i18n/timezones.txt.
Backport of 30e0a43937e685083fa1210c3594678a3b813806 from main.
---
docs/topics/i18n/timezones.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt
index 594c1688a566..8d5a82f17b04 100644
--- a/docs/topics/i18n/timezones.txt
+++ b/docs/topics/i18n/timezones.txt
@@ -207,7 +207,7 @@ Include a form in ``template.html`` that will ``POST`` to this view:
{% csrf_token %}
From d05cf7c35ff5fc2781962f06051c0df072201a78 Mon Sep 17 00:00:00 2001
From: Clifford Gama
Date: Thu, 16 Jan 2025 14:05:50 +0200
Subject: [PATCH 10/25] [5.1.x] Fixed #36078 -- Doc'd that Postgres normalizes
a range field with no points to empty.
Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Backport of 611e7bc3a0633a35ae3430e359c646e02fa3801d from main.
---
docs/ref/contrib/postgres/fields.txt | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt
index ec767b50e91a..65bff7f9e615 100644
--- a/docs/ref/contrib/postgres/fields.txt
+++ b/docs/ref/contrib/postgres/fields.txt
@@ -517,6 +517,15 @@ excluded, that is ``[)`` (see the PostgreSQL documentation for details about
fields (:class:`.DateTimeRangeField` and :class:`.DecimalRangeField`) by using
the ``default_bounds`` argument.
+.. admonition:: PostgreSQL normalizes a range with no points to the empty range
+
+ A range with equal values specified for an included lower bound and an
+ excluded upper bound, such as ``Range(datetime.date(2005, 6, 21),
+ datetime.date(2005, 6, 21))`` or ``[4, 4)``, has no points. PostgreSQL will
+ normalize the value to empty when saving to the database, and the original
+ bound values will be lost. See the `PostgreSQL documentation for details
+ `_.
+
``IntegerRangeField``
---------------------
From e9acb05b6308e2b07f7f75d70e80e6472a04a336 Mon Sep 17 00:00:00 2001
From: Clifford Gama <53076065+cliff688@users.noreply.github.com>
Date: Tue, 18 Mar 2025 22:54:10 +0200
Subject: [PATCH 11/25] [5.1.x] Fixed #36202 -- Added examples of JSONField
__contains and __contained_by lookups with nested arrays to docs.
Backport of 304e9f3d6ae8387bbfc261d68b51247a1f5230bb from main
---
docs/topics/db/queries.txt | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 2c2314198f83..e0551d27570f 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -1247,10 +1247,15 @@ contained in the top-level of the field. For example:
>>> Dog.objects.create(name="Fred", data={})
+ >>> Dog.objects.create(
+ ... name="Merry", data={"breed": "pekingese", "tricks": ["fetch", "dance"]}
+ ... )
>>> Dog.objects.filter(data__contains={"owner": "Bob"})
, ]>
>>> Dog.objects.filter(data__contains={"breed": "collie"})
]>
+ >>> Dog.objects.filter(data__contains={"tricks": ["dance"]})
+ ]>
.. admonition:: Oracle and SQLite
@@ -1273,10 +1278,17 @@ subset of those in the value passed. For example:
>>> Dog.objects.create(name="Fred", data={})
+ >>> Dog.objects.create(
+ ... name="Merry", data={"breed": "pekingese", "tricks": ["fetch", "dance"]}
+ ... )
>>> Dog.objects.filter(data__contained_by={"breed": "collie", "owner": "Bob"})
, ]>
>>> Dog.objects.filter(data__contained_by={"breed": "collie"})
]>
+ >>> Dog.objects.filter(
+ ... data__contained_by={"breed": "pekingese", "tricks": ["dance", "fetch", "hug"]}
+ ... )
+ , ]>
.. admonition:: Oracle and SQLite
From ab4bb5b2f93d5bb4e1375594adad9d8c596fb8a7 Mon Sep 17 00:00:00 2001
From: Carlton Gibson
Date: Thu, 13 Mar 2025 10:23:00 +0100
Subject: [PATCH 12/25] [5.1.x] Fixed #33497 -- Doc'd that persistent DB
connections should be disabled in ASGI and async modes.
Backport of 8713e4ae96817a0c7be3f7a8fee25a7c7f819721 from main.
---
docs/ref/databases.txt | 4 ++++
docs/topics/async.txt | 5 +++++
2 files changed, 9 insertions(+)
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index fff8f928fc6e..b8dfcea2b79a 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -40,6 +40,10 @@ database connection at the end of each request. To enable persistent
connections, set :setting:`CONN_MAX_AGE` to a positive integer of seconds. For
unlimited persistent connections, set it to ``None``.
+When using ASGI, persistent connections should be disabled. Instead, use your
+database backend's built-in connection pooling if available, or investigate a
+third-party connection pooling option if required.
+
Connection management
~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/topics/async.txt b/docs/topics/async.txt
index 87550ff46dea..204d79fc8c87 100644
--- a/docs/topics/async.txt
+++ b/docs/topics/async.txt
@@ -148,6 +148,11 @@ Transactions do not yet work in async mode. If you have a piece of code that
needs transactions behavior, we recommend you write that piece as a single
synchronous function and call it using :func:`sync_to_async`.
+:ref:`Persistent database connections `, set
+via the :setting:`CONN_MAX_AGE` setting, should also be disabled in async mode.
+Instead, use your database backend's built-in connection pooling if available,
+or investigate a third-party connection pooling option if required.
+
.. _async_performance:
Performance
From bd8bbc8c1a5971ce9cea325357234b119b91802a Mon Sep 17 00:00:00 2001
From: Clifford Gama
Date: Mon, 13 Jan 2025 21:14:25 +0200
Subject: [PATCH 13/25] [5.1.x] Refs #36095 -- Doc'd that
ManyToManyField.through supports lazy relationships.
Backport of eb4ea9c3efca479b169bed88a5521c4cf47ed2a2 from main.
---
docs/ref/models/fields.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 7937baf031ca..04359ad9208a 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -2005,6 +2005,10 @@ that control how the relationship functions.
the Django model that represents the intermediate table that you want to
use.
+ The ``through`` model can be specified using either the model class
+ directly or a :ref:`lazy reference ` to the model
+ class.
+
The most common use for this option is when you want to associate
:ref:`extra data with a many-to-many relationship
`.
From f927c9f2aac2d41ddbb7454da6470a9e93b26c38 Mon Sep 17 00:00:00 2001
From: Clifford Gama
Date: Mon, 13 Jan 2025 21:15:57 +0200
Subject: [PATCH 14/25] [5.1.x] Fixed #36095 -- Introduced lazy references in
"Models across files" section.
Backport of 6a2c296e706a0b8f9f9b89e66b37001ce2a03ea7 from main.
---
docs/topics/db/models.txt | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
index b543fd6759f9..5e7fe4b52e4a 100644
--- a/docs/topics/db/models.txt
+++ b/docs/topics/db/models.txt
@@ -724,6 +724,24 @@ refer to the other model class wherever needed. For example::
null=True,
)
+Alternatively, you can use a lazy reference to the related model, specified as
+a string in the format ``"app_label.ModelName"``. This does not require the
+related model to be imported. For example::
+
+ from django.db import models
+
+
+ class Restaurant(models.Model):
+ # ...
+ zip_code = models.ForeignKey(
+ "geography.ZipCode",
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ )
+
+See :ref:`lazy relationships ` for more details.
+
Field name restrictions
-----------------------
From f581b0b5c225059662236a86f935cba090672aa0 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 20 Mar 2025 08:31:06 +0100
Subject: [PATCH 15/25] [5.1.x] Documented the updating of translation catalogs
in post-release tasks.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Backport of 922c1c732a47c02aa5ef28b0b1a2bd9bc9b92d87 from main.
---
docs/internals/howto-release-django.txt | 32 +++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/docs/internals/howto-release-django.txt b/docs/internals/howto-release-django.txt
index 33df7474758b..a4a6e8f72de9 100644
--- a/docs/internals/howto-release-django.txt
+++ b/docs/internals/howto-release-django.txt
@@ -592,6 +592,38 @@ You're almost done! All that's left to do now is:
#. If this was a security release, update :doc:`/releases/security` with
details of the issues addressed.
+#. If this was a pre-release, the translation catalogs need to be updated:
+
+ #. Make a new branch from the recently released stable branch:
+
+ .. code-block:: shell
+
+ git checkout stable/A.B.x
+ git checkout -b update-translations-catalog-A.B.x
+
+ #. Ensure that the release's dedicated virtual environment is enabled and
+ run the following:
+
+ .. code-block:: shell
+
+ $ cd django
+ $ django-admin makemessages -l en --domain=djangojs --domain=django
+ processing locale en
+
+ #. Review the diff before pushing and avoid committing changes to the
+ ``.po`` files without any new translations (:commit:`example commit
+ `).
+
+ #. Make a pull request against the corresponding stable branch and merge
+ once approved.
+
+ #. Forward port the updated source translations to the ``main`` branch
+ (:commit:`example commit `).
+
+#. If this was an ``rc`` pre-release, call for translations for the upcoming
+ release in the `Django Forum - Internationalization category
+ `_.
+
.. _Trac's versions list: https://code.djangoproject.com/admin/ticket/versions
New stable branch tasks
From 659f88e4c96bea987b109754d22df8858f7e60d2 Mon Sep 17 00:00:00 2001
From: mguegnol <31782490+mguegnol@users.noreply.github.com>
Date: Sun, 23 Mar 2025 12:02:42 -0700
Subject: [PATCH 16/25] [5.1.x] Fixed typo in docs/topics/signals.txt.
Backport of e2b9a179133ebca9773c5c259f6a7d27489cf141 from main
---
docs/topics/signals.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt
index ea6989ed90ff..725d3a813842 100644
--- a/docs/topics/signals.txt
+++ b/docs/topics/signals.txt
@@ -318,7 +318,7 @@ Whether synchronous or asynchronous, receivers will be correctly adapted to
whether ``send()`` or ``asend()`` is used. Synchronous receivers will be
called using :func:`~.sync_to_async` when invoked via ``asend()``. Asynchronous
receivers will be called using :func:`~.async_to_sync` when invoked via
-``sync()``. Similar to the :ref:`case for middleware `,
+``send()``. Similar to the :ref:`case for middleware `,
there is a small performance cost to adapting receivers in this way. Note that
in order to reduce the number of sync/async calling-style switches within a
``send()`` or ``asend()`` call, the receivers are grouped by whether or not
From 3266f2516c27dd25abebe8e8f7b8778650ab4f18 Mon Sep 17 00:00:00 2001
From: dr-rompecabezas
Date: Sat, 22 Mar 2025 17:14:14 -0400
Subject: [PATCH 17/25] [5.1.x] Updated ogrinfo output in GIS tutorial.
Backport of fb65c520401d8eefb97725d16608444901cfed14 from main
---
docs/ref/contrib/gis/tutorial.txt | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt
index b051a57a0f3f..17635d2e24b8 100644
--- a/docs/ref/contrib/gis/tutorial.txt
+++ b/docs/ref/contrib/gis/tutorial.txt
@@ -158,22 +158,34 @@ and use the ``-so`` option to get only the important summary information:
using driver `ESRI Shapefile' successful.
Layer name: TM_WORLD_BORDERS-0.3
+ Metadata:
+ DBF_DATE_LAST_UPDATE=2008-07-30
Geometry: Polygon
Feature Count: 246
Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
Layer SRS WKT:
- GEOGCS["GCS_WGS_1984",
- DATUM["WGS_1984",
- SPHEROID["WGS_1984",6378137.0,298.257223563]],
- PRIMEM["Greenwich",0.0],
- UNIT["Degree",0.0174532925199433]]
+ GEOGCRS["WGS 84",
+ DATUM["World Geodetic System 1984",
+ ELLIPSOID["WGS 84",6378137,298.257223563,
+ LENGTHUNIT["metre",1]]],
+ PRIMEM["Greenwich",0,
+ ANGLEUNIT["degree",0.0174532925199433]],
+ CS[ellipsoidal,2],
+ AXIS["latitude",north,
+ ORDER[1],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ AXIS["longitude",east,
+ ORDER[2],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4326]]
+ Data axis to CRS axis mapping: 2,1
FIPS: String (2.0)
ISO2: String (2.0)
ISO3: String (3.0)
UN: Integer (3.0)
NAME: String (50.0)
AREA: Integer (7.0)
- POP2005: Integer (10.0)
+ POP2005: Integer64 (10.0)
REGION: Integer (3.0)
SUBREGION: Integer (3.0)
LON: Real (8.3)
From e38a80773d4eace4d41628154c68fe3c902258c2 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Tue, 25 Mar 2025 17:19:07 +0100
Subject: [PATCH 18/25] [5.1.x] Pinned black == 24.10.0 in GitHub actions,
pre-commit and test requirements.
---
.github/workflows/linters.yml | 2 +-
.pre-commit-config.yaml | 4 ++--
tests/requirements/py3.txt | 2 +-
tox.ini | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index 7c64dc98ff4c..4f24db61f01e 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -59,4 +59,4 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: black
- uses: psf/black@stable
+ uses: psf/black@24.10.0
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d1c74a66c818..f4a1b84ae4dc 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
- rev: 24.2.0
+ rev: 24.10.0
hooks:
- id: black
exclude: \.py-tpl$
@@ -9,7 +9,7 @@ repos:
hooks:
- id: blacken-docs
additional_dependencies:
- - black==24.2.0
+ - black==24.10.0
files: 'docs/.*\.txt$'
args: ["--rst-literal-block"]
- repo: https://github.com/PyCQA/isort
diff --git a/tests/requirements/py3.txt b/tests/requirements/py3.txt
index a9679af97ca6..26d4b982fe90 100644
--- a/tests/requirements/py3.txt
+++ b/tests/requirements/py3.txt
@@ -2,7 +2,7 @@ aiosmtpd
asgiref >= 3.8.1
argon2-cffi >= 19.2.0
bcrypt
-black
+black == 24.10.0
docutils >= 0.19
geoip2
jinja2 >= 2.11.0
diff --git a/tox.ini b/tox.ini
index 7a76693f2116..57da70cd092f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -37,7 +37,7 @@ commands =
[testenv:black]
basepython = python3
usedevelop = false
-deps = black
+deps = black == 24.10.0
changedir = {toxinidir}
commands = black --check --diff .
From 451ba1f3cfc7aaf7d5e546cb3bd2fef35d4b9c2e Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Wed, 26 Mar 2025 09:00:27 +0100
Subject: [PATCH 19/25] [5.1.x] Added stub release notes and release date for
5.1.8 and 5.0.14.
Backport of c75fbe843079ca249d7015926490dd21107e63a4 from main.
---
docs/releases/5.0.14.txt | 7 +++++++
docs/releases/5.1.8.txt | 5 +++--
docs/releases/index.txt | 1 +
3 files changed, 11 insertions(+), 2 deletions(-)
create mode 100644 docs/releases/5.0.14.txt
diff --git a/docs/releases/5.0.14.txt b/docs/releases/5.0.14.txt
new file mode 100644
index 000000000000..8684a270dc13
--- /dev/null
+++ b/docs/releases/5.0.14.txt
@@ -0,0 +1,7 @@
+===========================
+Django 5.0.14 release notes
+===========================
+
+*April 2, 2025*
+
+Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
diff --git a/docs/releases/5.1.8.txt b/docs/releases/5.1.8.txt
index e5143904a260..2aa6686cb69c 100644
--- a/docs/releases/5.1.8.txt
+++ b/docs/releases/5.1.8.txt
@@ -2,9 +2,10 @@
Django 5.1.8 release notes
==========================
-*Expected April 2, 2025*
+*April 2, 2025*
-Django 5.1.8 fixes several bugs in 5.1.7.
+Django 5.1.8 fixes a security issue with severity "moderate" and several bugs
+in 5.1.7.
Bugfixes
========
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 04f425ff6623..3f7b52d86f5a 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -40,6 +40,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 5.0.14
5.0.13
5.0.12
5.0.11
From 31262b37d48905794da6fe3b374f3ab0808699ca Mon Sep 17 00:00:00 2001
From: Carlton Gibson
Date: Sat, 22 Mar 2025 14:27:36 +0100
Subject: [PATCH 20/25] [5.1.x] Doc'd how to use Intersphinx in the reusable
apps tutorial.
Backport of 6e54e20cc3908d4eb103678db14e1e02e05069dd from main.
---
docs/intro/reusable-apps.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt
index 5acf8c2b182a..71696ce0ee8d 100644
--- a/docs/intro/reusable-apps.txt
+++ b/docs/intro/reusable-apps.txt
@@ -264,6 +264,26 @@ this. For a small app like polls, this process isn't too difficult.
you add some files to it. Many Django apps also provide their documentation
online through sites like `readthedocs.org `_.
+ Many Python projects, including Django and Python itself, use `Sphinx
+ `_ to build
+ their documentation. If you choose to use Sphinx you can link back to the
+ Django documentation by configuring `Intersphinx
+ `_
+ and including a value for Django in your project's ``intersphinx_mapping``
+ value::
+
+ intersphinx_mapping = {
+ # ...
+ "django": (
+ "https://docs.djangoproject.com/en/stable/",
+ "https://docs.djangoproject.com/en/stable/_objects/",
+ ),
+ }
+
+ With that in place, you can then cross-link to specific entries, in the
+ same way as in the Django docs, such as
+ "``:attr:`django.test.TransactionTestCase.databases```".
+
#. Check that the :pypi:`build` package is installed (``python -m pip install
build``) and try building your package by running ``python -m build`` inside
``django-polls``. This creates a directory called ``dist`` and builds your
From 5805d1c346bc7875259abb47626b268ba4479881 Mon Sep 17 00:00:00 2001
From: Carlton Gibson
Date: Sat, 22 Mar 2025 14:27:36 +0100
Subject: [PATCH 21/25] [5.1.x] Simplified Intersphinx configuration example.
docs.djangoproject.com had been updated to serve the object.inv file
from the default location, so the second tuple element can be None
(the "default" value).
Backport of 5df512e53ab12fd8a0c92421a45aa1b664adb166 from main.
---
docs/intro/reusable-apps.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt
index 71696ce0ee8d..3defa74c4f62 100644
--- a/docs/intro/reusable-apps.txt
+++ b/docs/intro/reusable-apps.txt
@@ -276,7 +276,7 @@ this. For a small app like polls, this process isn't too difficult.
# ...
"django": (
"https://docs.djangoproject.com/en/stable/",
- "https://docs.djangoproject.com/en/stable/_objects/",
+ None,
),
}
From 3fdc8c31da5d5198ee9bdc33a47c70f78bf0d190 Mon Sep 17 00:00:00 2001
From: Clifford Gama
Date: Mon, 31 Mar 2025 11:52:48 +0200
Subject: [PATCH 22/25] [5.1.x] Clarified pre_delete and post_delete's origin
attributes.
Backport of 9d5d0e8135a9654aa289cf922fcd00ad5e2a7fe5 from main.
---
docs/ref/signals.txt | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt
index 953b18c1f691..65f3833913b9 100644
--- a/docs/ref/signals.txt
+++ b/docs/ref/signals.txt
@@ -204,9 +204,8 @@ Arguments sent with this signal:
The database alias being used.
``origin``
-
- The origin of the deletion being the instance of a ``Model`` or
- ``QuerySet`` class.
+ The ``Model`` or ``QuerySet`` instance from which the deletion originated,
+ that is, the instance whose ``delete()`` method was invoked.
``post_delete``
---------------
@@ -233,9 +232,8 @@ Arguments sent with this signal:
The database alias being used.
``origin``
-
- The origin of the deletion being the instance of a ``Model`` or
- ``QuerySet`` class.
+ The ``Model`` or ``QuerySet`` instance from which the deletion originated,
+ that is, the instance whose ``delete()`` method was invoked.
``m2m_changed``
---------------
From b3b09dc6ce72f2aa778b95dc988653bf8c034035 Mon Sep 17 00:00:00 2001
From: Babak Mahmoudy
Date: Tue, 1 Apr 2025 19:34:59 +1100
Subject: [PATCH 23/25] [5.1.x] Fixed #36213 -- Doc'd MySQL's handling of
self-select updates in QuerySet.update().
Co-authored-by: Andro Ranogajec
Backport of be1b776ad8d6f9bccfbdf63f84b16fb81a13119e from main.
---
docs/ref/models/querysets.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 71af4c58820b..0ac8964e4080 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2977,6 +2977,14 @@ Using ``update()`` also prevents a race condition wherein something might
change in your database in the short period of time between loading the object
and calling ``save()``.
+.. admonition:: MySQL does not support self-select updates
+
+ On MySQL, ``QuerySet.update()`` may execute a ``SELECT`` followed by an
+ ``UPDATE`` instead of a single ``UPDATE`` when filtering on related tables,
+ which can introduce a race condition if concurrent changes occur between
+ the queries. To ensure atomicity, consider using transactions or avoiding
+ such filter conditions on MySQL.
+
Finally, realize that ``update()`` does an update at the SQL level and, thus,
does not call any ``save()`` methods on your models, nor does it emit the
:attr:`~django.db.models.signals.pre_save` or
From edc2716d01a6fdd84b173c02031695231bcee1f8 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Thu, 6 Mar 2025 15:24:56 +0100
Subject: [PATCH 24/25] [5.1.x] Fixed CVE-2025-27556 -- Mitigated potential DoS
in url_has_allowed_host_and_scheme() on Windows.
Thank you sw0rd1ight for the report.
Backport of 39e2297210d9d2938c75fc911d45f0e863dc4821 from main.
---
django/core/validators.py | 3 ++-
django/utils/html.py | 3 +--
django/utils/http.py | 6 +++++-
docs/releases/5.0.14.txt | 10 ++++++++++
docs/releases/5.1.8.txt | 10 ++++++++++
tests/utils_tests/test_http.py | 16 ++++++++++++++++
6 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/django/core/validators.py b/django/core/validators.py
index 8732ddf7adbf..2979f3aefd4a 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -7,6 +7,7 @@
from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.encoding import punycode
+from django.utils.http import MAX_URL_LENGTH
from django.utils.ipv6 import is_valid_ipv6_address
from django.utils.regex_helper import _lazy_re_compile
from django.utils.translation import gettext_lazy as _
@@ -155,7 +156,7 @@ class URLValidator(RegexValidator):
message = _("Enter a valid URL.")
schemes = ["http", "https", "ftp", "ftps"]
unsafe_chars = frozenset("\t\r\n")
- max_length = 2048
+ max_length = MAX_URL_LENGTH
def __init__(self, schemes=None, **kwargs):
super().__init__(**kwargs)
diff --git a/django/utils/html.py b/django/utils/html.py
index ff8684f5a974..8892b9a740fb 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -11,7 +11,7 @@
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.encoding import punycode
from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
-from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
+from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import SafeData, SafeString, mark_safe
from django.utils.text import normalize_newlines
@@ -39,7 +39,6 @@
)
)
-MAX_URL_LENGTH = 2048
MAX_STRIP_TAGS_DEPTH = 50
diff --git a/django/utils/http.py b/django/utils/http.py
index 78dfee7feecf..87c2ac0d64c2 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -37,6 +37,7 @@
RFC3986_GENDELIMS = ":/?#[]@"
RFC3986_SUBDELIMS = "!$&'()*+,;="
+MAX_URL_LENGTH = 2048
def urlencode(query, doseq=False):
@@ -272,7 +273,10 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
# Chrome considers any URL with more than two slashes to be absolute, but
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
- if url.startswith("///"):
+ if url.startswith("///") or len(url) > MAX_URL_LENGTH:
+ # urlparse does not perform validation of inputs. Unicode normalization
+ # is very slow on Windows and can be a DoS attack vector.
+ # https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
return False
try:
url_info = urlparse(url)
diff --git a/docs/releases/5.0.14.txt b/docs/releases/5.0.14.txt
index 8684a270dc13..230bfed652e0 100644
--- a/docs/releases/5.0.14.txt
+++ b/docs/releases/5.0.14.txt
@@ -5,3 +5,13 @@ Django 5.0.14 release notes
*April 2, 2025*
Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
+
+CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
+=============================================================================================================================
+
+Python's :func:`NFKC normalization ` is slow on
+Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
+:class:`~django.contrib.auth.views.LogoutView`, and
+:func:`~django.views.i18n.set_language` were subject to a potential
+denial-of-service attack via certain inputs with a very large number of Unicode
+characters.
diff --git a/docs/releases/5.1.8.txt b/docs/releases/5.1.8.txt
index 2aa6686cb69c..b3b0327b52cc 100644
--- a/docs/releases/5.1.8.txt
+++ b/docs/releases/5.1.8.txt
@@ -7,6 +7,16 @@ Django 5.1.8 release notes
Django 5.1.8 fixes a security issue with severity "moderate" and several bugs
in 5.1.7.
+CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
+=============================================================================================================================
+
+Python's :func:`NFKC normalization ` is slow on
+Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
+:class:`~django.contrib.auth.views.LogoutView`, and
+:func:`~django.views.i18n.set_language` were subject to a potential
+denial-of-service attack via certain inputs with a very large number of Unicode
+characters.
+
Bugfixes
========
diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py
index 68df04696aa6..838ff7e1001b 100644
--- a/tests/utils_tests/test_http.py
+++ b/tests/utils_tests/test_http.py
@@ -6,6 +6,7 @@
from django.test import SimpleTestCase
from django.utils.datastructures import MultiValueDict
from django.utils.http import (
+ MAX_URL_LENGTH,
base36_to_int,
content_disposition_header,
escape_leading_slashes,
@@ -273,6 +274,21 @@ def test_secure_param_non_https_urls(self):
False,
)
+ def test_max_url_length(self):
+ allowed_host = "example.com"
+ max_extra_characters = "é" * (MAX_URL_LENGTH - len(allowed_host) - 1)
+ max_length_boundary_url = f"{allowed_host}/{max_extra_characters}"
+ cases = [
+ (max_length_boundary_url, True),
+ (max_length_boundary_url + "ú", False),
+ ]
+ for url, expected in cases:
+ with self.subTest(url=url):
+ self.assertIs(
+ url_has_allowed_host_and_scheme(url, allowed_hosts={allowed_host}),
+ expected,
+ )
+
class URLSafeBase64Tests(unittest.TestCase):
def test_roundtrip(self):
From 5773bc9cf929f4648da30cb9c472b816bb4db771 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Wed, 2 Apr 2025 10:29:55 +0200
Subject: [PATCH 25/25] [5.1.x] Bumped version for 5.1.8 release.
---
django/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/django/__init__.py b/django/__init__.py
index fa2ee8caf2c0..2e50d1c814cb 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,6 +1,6 @@
from django.utils.version import get_version
-VERSION = (5, 1, 8, "alpha", 0)
+VERSION = (5, 1, 8, "final", 0)
__version__ = get_version(VERSION)