From 6e89271a8507fe272d11814975500a1b40303a04 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Mon, 15 Sep 2025 03:35:53 +1000 Subject: [PATCH 1/2] Refs #27489 -- Made RenamePermission() operation respect database. Regression in f02b49d2f3bf84f5225de920ca510149f1f9f1da. Co-authored-by: Mariusz Felisiak --- django/contrib/auth/management/__init__.py | 4 ++-- tests/auth_tests/test_management.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index 40ada52e29df..21d357e32b88 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -126,10 +126,10 @@ def _rename(self, apps, schema_editor, old_model, new_model): from django.contrib.auth.models import Permission db = schema_editor.connection.alias - ctypes = ContentType.objects.filter( + ctypes = ContentType.objects.using(db).filter( app_label=self.app_label, model__icontains=old_model.lower() ) - for permission in Permission.objects.filter( + for permission in Permission.objects.using(db).filter( content_type_id__in=ctypes.values("id") ): prefix = permission.codename.split("_")[0] diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py index 7e8d01cbce12..46fe310db20c 100644 --- a/tests/auth_tests/test_management.py +++ b/tests/auth_tests/test_management.py @@ -1546,6 +1546,7 @@ class PermissionRenameOperationsTests(TransactionTestCase): "django.contrib.auth", "auth_tests", ] + databases = {"default", "other"} def setUp(self): app_config = apps.get_app_config("auth_tests") @@ -1605,6 +1606,26 @@ def test_permission_rename(self): Permission.objects.filter(codename=f"{action}_newmodel").exists() ) + def test_permission_rename_other_db(self): + ct = ContentType.objects.using("default").create( + app_label="auth_tests", model="oldmodel" + ) + permission = Permission.objects.using("default").create( + codename="add_oldmodel", + name="Can add old model", + content_type=ct, + ) + # RenamePermission respects the database. + call_command("migrate", "auth_tests", verbosity=0, database="other") + permission.refresh_from_db() + self.assertEqual(permission.codename, "add_oldmodel") + self.assertFalse( + Permission.objects.using("other").filter(codename="add_oldmodel").exists() + ) + self.assertTrue( + Permission.objects.using("other").filter(codename="add_newmodel").exists() + ) + @mock.patch( "django.db.router.allow_migrate_model", return_value=False, From 0e0b4214c350da9b627a67987b13ec334e1de033 Mon Sep 17 00:00:00 2001 From: Caitie Baca Date: Thu, 11 Sep 2025 13:06:47 -0700 Subject: [PATCH 2/2] Fixed #36589 -- Made assertTemplateUsed/NotUsed track full path for PartialTemplate. Previously, assertTemplateUsed only matched partial names, ignoring the template origin. This caused assertions on partials specified by origin ("template.html#partial") to fail. Refs #36410. --- AUTHORS | 1 + django/test/testcases.py | 20 ++++++-- .../templates/template_used/partials.html | 3 ++ tests/test_utils/tests.py | 47 +++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 tests/test_utils/templates/template_used/partials.html diff --git a/AUTHORS b/AUTHORS index 044391e4a324..4b1cc7d35754 100644 --- a/AUTHORS +++ b/AUTHORS @@ -196,6 +196,7 @@ answer newbie questions, and generally made Django that much better: btoll@bestweb.net C8E Caio Ariede + Caitie Baca Calvin Spealman Cameron Curry Cameron Knight (ckknight) diff --git a/django/test/testcases.py b/django/test/testcases.py index 5f0c81981556..c587f770a6a9 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -43,6 +43,7 @@ from django.forms.fields import CharField from django.http import QueryDict from django.http.request import split_domain_port, validate_host +from django.template.base import PartialTemplate from django.test.client import AsyncClient, Client from django.test.html import HTMLParseError, parse_html from django.test.signals import template_rendered @@ -138,10 +139,22 @@ def on_template_render(self, sender, signal, template, context, **kwargs): self.rendered_templates.append(template) self.context.append(copy(context)) + @property + def rendered_template_names(self): + return [ + ( + f"{t.origin.template_name}#{t.name}" + if isinstance(t, PartialTemplate) + else t.name + ) + for t in self.rendered_templates + if t.name is not None + ] + def test(self): self.test_case._assert_template_used( self.template_name, - [t.name for t in self.rendered_templates if t.name is not None], + self.rendered_template_names, self.msg_prefix, self.count, ) @@ -159,11 +172,8 @@ def __exit__(self, exc_type, exc_value, traceback): class _AssertTemplateNotUsedContext(_AssertTemplateUsedContext): def test(self): - rendered_template_names = [ - t.name for t in self.rendered_templates if t.name is not None - ] self.test_case.assertFalse( - self.template_name in rendered_template_names, + self.template_name in self.rendered_template_names, f"{self.msg_prefix}Template '{self.template_name}' was used " f"unexpectedly in rendering the response", ) diff --git a/tests/test_utils/templates/template_used/partials.html b/tests/test_utils/templates/template_used/partials.html new file mode 100644 index 000000000000..028c2d841784 --- /dev/null +++ b/tests/test_utils/templates/template_used/partials.html @@ -0,0 +1,3 @@ +{% partialdef hello %} +

Hello

+{% endpartialdef %} diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 9c22b61b4ff2..70cca3d4418d 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -646,6 +646,53 @@ def test_assert_used_on_http_response(self): self.assertTemplateNotUsed(response, "template.html") +@override_settings(ROOT_URLCONF="test_utils.urls") +class AssertTemplateUsedPartialTests(SimpleTestCase): + def test_template_used_pass(self): + with self.assertTemplateUsed("template_used/partials.html#hello"): + render_to_string("template_used/partials.html#hello") + + def test_template_not_used_pass(self): + with self.assertTemplateNotUsed("hello"): + render_to_string("template_used/partials.html#hello") + + def test_template_used_fail(self): + msg = "Template 'hello' was not a template used to render the response." + with ( + self.assertRaisesMessage(AssertionError, msg), + self.assertTemplateUsed("hello"), + ): + render_to_string("template_used/base.html") + + def test_template_not_used_fail(self): + msg = ( + "Template 'template_used/partials.html#hello' was used " + "unexpectedly in rendering the response" + ) + with ( + self.assertRaisesMessage(AssertionError, msg), + self.assertTemplateNotUsed("template_used/partials.html#hello"), + ): + render_to_string("template_used/partials.html#hello") + + def test_template_not_used_pass_non_partial(self): + with self.assertTemplateNotUsed( + "template_used/base.html#template_used/base.html" + ): + render_to_string("template_used/base.html") + + def test_template_used_fail_non_partial(self): + msg = ( + "Template 'template_used/base.html#template_used/base.html' was not a " + "template used to render the response." + ) + with ( + self.assertRaisesMessage(AssertionError, msg), + self.assertTemplateUsed("template_used/base.html#template_used/base.html"), + ): + render_to_string("template_used/base.html") + + class HTMLEqualTests(SimpleTestCase): def test_html_parser(self): element = parse_html("

Hello

")