From 04613791af21a124299e0883bcbd6bd3fa0ab51f Mon Sep 17 00:00:00 2001 From: jun Date: Sat, 21 Feb 2026 19:35:11 +0900 Subject: [PATCH 1/4] Fixed a typo in tests/i18n/tests.py. --- tests/i18n/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index aac56f5df451..e593d97cba14 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -1160,7 +1160,7 @@ def test_l10n_enabled(self): def test_uncommon_locale_formats(self): testcases = { - # French Canadian locale uses 'h' as time format seperator. + # French Canadian locale uses 'h' as time format separator. ("fr-ca", time_format, (self.t, "TIME_FORMAT")): "10\xa0h\xa015", ( "fr-ca", From f45bfa5e2801545c1c8787f59bee3867c9928871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Aur=C3=A9lio=20da=20Rosa=20Haubrich?= Date: Wed, 18 Feb 2026 17:11:14 -0300 Subject: [PATCH 2/4] Fixed #36935 -- Added fallback in ContentType.app_labeled_name when model_class() is None. Updated ContentType.app_labeled_name to include the app_label in its string representation. This removed ambiguity for content types whose models were not present in the current codebase (for example, when multiple applications share the same database). Adjusted related tests to reflect the new representation. --- django/contrib/contenttypes/models.py | 2 +- tests/contenttypes_tests/test_models.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index 1ae45dea95ba..f634976a9301 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -156,7 +156,7 @@ def name(self): def app_labeled_name(self): model = self.model_class() if not model: - return self.model + return "%s | %s" % (self.app_label, self.model) return "%s | %s" % ( model._meta.app_config.verbose_name, model._meta.verbose_name, diff --git a/tests/contenttypes_tests/test_models.py b/tests/contenttypes_tests/test_models.py index b63c57ef09fa..542f0c8fd8d4 100644 --- a/tests/contenttypes_tests/test_models.py +++ b/tests/contenttypes_tests/test_models.py @@ -145,7 +145,7 @@ class ModelCreatedOnTheFly(models.Model): ct = ContentType.objects.get_for_model(ModelCreatedOnTheFly) self.assertEqual(ct.app_label, "contenttypes_tests") self.assertEqual(ct.model, "modelcreatedonthefly") - self.assertEqual(str(ct), "modelcreatedonthefly") + self.assertEqual(str(ct), "contenttypes_tests | modelcreatedonthefly") def test_get_for_concrete_model(self): """ @@ -269,7 +269,7 @@ def test_missing_model(self): app_label="contenttypes", model="OldModel", ) - self.assertEqual(str(ct), "OldModel") + self.assertEqual(str(ct), "contenttypes | OldModel") self.assertIsNone(ct.model_class()) # Stale ContentTypes can be fetched like any other object. @@ -318,7 +318,7 @@ def test_name_unknown_model(self): def test_app_labeled_name_unknown_model(self): ct = ContentType(app_label="contenttypes_tests", model="unknown") - self.assertEqual(ct.app_labeled_name, "unknown") + self.assertEqual(ct.app_labeled_name, "contenttypes_tests | unknown") class TestRouter: From e85db77e11e37d6ec82526557c4dafe4b30dc699 Mon Sep 17 00:00:00 2001 From: Saish Mungase Date: Thu, 19 Feb 2026 22:50:08 +0530 Subject: [PATCH 3/4] Fixed #36937 -- Added missing positive permission cases in test_client tests. --- tests/test_client/tests.py | 22 +++++++++++++++++++--- tests/test_client/views.py | 6 +++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index d1d329aff963..2c2e394b20ae 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -25,7 +25,7 @@ import tempfile from unittest import mock -from django.contrib.auth.models import User +from django.contrib.auth.models import Permission, User from django.core import mail from django.http import HttpResponse, HttpResponseNotAllowed from django.test import ( @@ -814,7 +814,15 @@ def test_view_with_permissions(self): response, "/accounts/login/?next=/permission_protected_view/" ) - # TODO: Log in with right permissions and request the page again + permission = Permission.objects.get( + content_type__app_label="auth", codename="add_user" + ) + self.u1.user_permissions.add(permission) + + # Request the page again. Access is granted. + response = self.client.get("/permission_protected_view/") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context["user"].username, "testclient") def test_view_with_permissions_exception(self): """ @@ -853,7 +861,15 @@ def test_view_with_method_permissions(self): response, "/accounts/login/?next=/permission_protected_method_view/" ) - # TODO: Log in with right permissions and request the page again + permission = Permission.objects.get( + content_type__app_label="auth", codename="add_user" + ) + self.u1.user_permissions.add(permission) + + # Request the page again. Access is granted. + response = self.client.get("/permission_protected_method_view/") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context["user"].username, "testclient") def test_external_redirect(self): response = self.client.get("/django_project_redirect/") diff --git a/tests/test_client/views.py b/tests/test_client/views.py index 96e0142dd315..e1691ad56ddd 100644 --- a/tests/test_client/views.py +++ b/tests/test_client/views.py @@ -304,11 +304,11 @@ def _permission_protected_view(request): return HttpResponse(t.render(c)) -permission_protected_view = permission_required("permission_not_granted")( +permission_protected_view = permission_required("auth.add_user")( _permission_protected_view ) permission_protected_view_exception = permission_required( - "permission_not_granted", raise_exception=True + "auth.add_user", raise_exception=True )(_permission_protected_view) @@ -323,7 +323,7 @@ def login_protected_view(self, request): c = Context({"user": request.user}) return HttpResponse(t.render(c)) - @method_decorator(permission_required("permission_not_granted")) + @method_decorator(permission_required("auth.add_user")) def permission_protected_view(self, request): t = Template( "This is a permission protected test using a method. " From 158fd81ef5a5647d27eb3065063284f9ee0a3ca4 Mon Sep 17 00:00:00 2001 From: Amar <100243770+aadeina@users.noreply.github.com> Date: Tue, 3 Feb 2026 00:34:04 +0000 Subject: [PATCH 4/4] Fixed #36899 -- Implemented SessionBase.__bool__. --- django/contrib/sessions/backends/base.py | 3 +++ docs/releases/6.1.txt | 4 +++- docs/topics/http/sessions.txt | 11 +++++++++++ tests/sessions_tests/tests.py | 11 +++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index e53f1d201a1f..8f673761f2eb 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -66,6 +66,9 @@ def __delitem__(self, key): del self._session[key] self.modified = True + def __bool__(self): + return not self.is_empty() + @property def key_salt(self): return "django.contrib.sessions." + self.__class__.__qualname__ diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 756dcf3395d3..6c6890b811ad 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -150,7 +150,9 @@ Minor features :mod:`django.contrib.sessions` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* :class:`~django.contrib.sessions.backends.base.SessionBase` now supports + boolean evaluation via + :meth:`~django.contrib.sessions.backends.base.SessionBase.__bool__`. :mod:`django.contrib.sitemaps` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index 797b49ffa202..6070b3e4ef40 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -191,6 +191,17 @@ You can edit it multiple times. Example: ``'fav_color' in request.session`` + .. method:: __bool__() + + .. versionadded:: 6.1 + + Returns the inverse of :meth:`is_empty`. This allows checking if a + session has data:: + + if request.session: + # Session has data or a key + pass + .. method:: get(key, default=None) .. method:: aget(key, default=None) diff --git a/tests/sessions_tests/tests.py b/tests/sessions_tests/tests.py index 81c2e7a5de39..f23fa9778d16 100644 --- a/tests/sessions_tests/tests.py +++ b/tests/sessions_tests/tests.py @@ -1373,3 +1373,14 @@ async def test_atest_cookie(self): def test_is_empty(self): self.assertIs(self.session.is_empty(), True) + + def test_bool(self): + # Empty session is falsy + self.assertIs(bool(self.session), False) + # Session with data is truthy + self.session["foo"] = "bar" + self.assertIs(bool(self.session), True) + # Session with key but no data is truthy + session_with_key = SessionBase() + session_with_key._session_key = "testkey1234" + self.assertIs(bool(session_with_key), True)