From e2dfa81ff7489d97700604d634adacf1384af184 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 15 Jul 2016 11:07:43 -0400 Subject: [PATCH] Refs #18682 -- Edited explanation in stale content type deletion. Follow up to 8db889eaf7dce0cb715b075be32047c1b1b316da. --- django/contrib/contenttypes/management.py | 19 ++++++++----------- docs/releases/1.11.txt | 7 +++---- tests/contenttypes_tests/models.py | 1 + tests/contenttypes_tests/tests.py | 16 +++++++++++----- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/django/contrib/contenttypes/management.py b/django/contrib/contenttypes/management.py index 591f96705b..aa1c198fc4 100644 --- a/django/contrib/contenttypes/management.py +++ b/django/contrib/contenttypes/management.py @@ -154,22 +154,20 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT for obj_type, objs in collector.data.items(): if objs == {ct}: continue - ct_info.append(' - %s object%s of type %s.%s:' % ( + ct_info.append(' - %s %s object(s)' % ( len(objs), - 's' if len(objs) != 1 else '', - obj_type._meta.app_label, - obj_type._meta.model_name) - ) + obj_type._meta.label, + )) content_type_display = '\n'.join(ct_info) print("""Some content types in your database are stale and can be deleted. -Any objects that depend on these content types will then also be deleted. -The content types, and the dependent objects that would be deleted, are: +Any objects that depend on these content types will also be deleted. +The content types and dependent objects that would be deleted are: %s -This list does not include data that might be in your database -outside of Django's models. +This list doesn't include any cascade deletions to data outside of Django's +models (uncommon). Are you sure you want to delete these content types? If you're unsure, answer 'no'. @@ -191,7 +189,6 @@ If you're unsure, answer 'no'. class NoFastDeleteCollector(Collector): def can_fast_delete(self, *args, **kwargs): """ - We always want to load the objects into memory so that we can display - them to the user when asking confirmation. + Always load related objects to display them when showing confirmation. """ return False diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 64a5fc2780..9d2ccb0f6b 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -87,10 +87,9 @@ Minor features :mod:`django.contrib.contenttypes` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* When stale content types are detected during a management command, there is - now an expansive list of objects that will be deleted. Previously, only - the content type objects themselves were listed, even if there were objects - with foreign keys towards the content types that would be deleted also. +* When stale content types are detected after the ``migrate`` command, there's + now a list of related objects such as ``auth.Permission``\s that will also be + deleted. Previously, only the content types were listed. :mod:`django.contrib.gis` ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/contenttypes_tests/models.py b/tests/contenttypes_tests/models.py index 4b9d3d31e6..fe1854e8a3 100644 --- a/tests/contenttypes_tests/models.py +++ b/tests/contenttypes_tests/models.py @@ -131,6 +131,7 @@ class Post(models.Model): class ModelWithNullFKToSite(models.Model): title = models.CharField(max_length=200) site = models.ForeignKey(Site, null=True, on_delete=models.CASCADE) + post = models.ForeignKey(Post, null=True, on_delete=models.CASCADE) def __str__(self): return self.title diff --git a/tests/contenttypes_tests/tests.py b/tests/contenttypes_tests/tests.py index 5c8c675a4a..6d57a23355 100644 --- a/tests/contenttypes_tests/tests.py +++ b/tests/contenttypes_tests/tests.py @@ -389,21 +389,27 @@ class UpdateContentTypesTests(TestCase): def test_interactive_true_with_dependent_objects(self): """ interactive mode of update_contenttypes() (the default) should delete - stale contenttypes and warn of dependent objects + stale contenttypes and warn of dependent objects. """ - Post.objects.create(title='post', content_type=self.content_type) + post = Post.objects.create(title='post', content_type=self.content_type) + # A related object is needed to show that a custom collector with + # can_fast_delete=False is needed. + ModelWithNullFKToSite.objects.create(post=post) contenttypes_management.input = lambda x: force_str("yes") with captured_stdout() as stdout: contenttypes_management.update_contenttypes(self.app_config) self.assertEqual(Post.objects.count(), 0) - self.assertIn("1 object of type contenttypes_tests.post:", stdout.getvalue()) - self.assertIn("Deleting stale content type", stdout.getvalue()) + output = stdout.getvalue() + self.assertIn('- Content type for contenttypes_tests.Fake', output) + self.assertIn('- 1 contenttypes_tests.Post object(s)', output) + self.assertIn('- 1 contenttypes_tests.ModelWithNullFKToSite', output) + self.assertIn('Deleting stale content type', output) self.assertEqual(ContentType.objects.count(), self.before_count) def test_interactive_true_without_dependent_objects(self): """ interactive mode of update_contenttypes() (the default) should delete - stale contenttypes and inform there are no dependent objects + stale contenttypes even if there aren't any dependent objects. """ contenttypes_management.input = lambda x: force_str("yes") with captured_stdout() as stdout: