Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion django/contrib/contenttypes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions django/contrib/sessions/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand Down
4 changes: 3 additions & 1 deletion docs/releases/6.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
11 changes: 11 additions & 0 deletions docs/topics/http/sessions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions tests/contenttypes_tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion tests/i18n/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 11 additions & 0 deletions tests/sessions_tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
22 changes: 19 additions & 3 deletions tests/test_client/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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/")
Expand Down
6 changes: 3 additions & 3 deletions tests/test_client/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand All @@ -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. "
Expand Down