Kaydet (Commit) e2dfa81f authored tarafından Tim Graham's avatar Tim Graham

Refs #18682 -- Edited explanation in stale content type deletion.

Follow up to 8db889ea.
üst 7399fee6
...@@ -154,22 +154,20 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT ...@@ -154,22 +154,20 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT
for obj_type, objs in collector.data.items(): for obj_type, objs in collector.data.items():
if objs == {ct}: if objs == {ct}:
continue continue
ct_info.append(' - %s object%s of type %s.%s:' % ( ct_info.append(' - %s %s object(s)' % (
len(objs), len(objs),
's' if len(objs) != 1 else '', obj_type._meta.label,
obj_type._meta.app_label, ))
obj_type._meta.model_name)
)
content_type_display = '\n'.join(ct_info) content_type_display = '\n'.join(ct_info)
print("""Some content types in your database are stale and can be deleted. 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. Any objects that depend on these content types will also be deleted.
The content types, and the dependent objects that would be deleted, are: The content types and dependent objects that would be deleted are:
%s %s
This list does not include data that might be in your database This list doesn't include any cascade deletions to data outside of Django's
outside of Django's models. models (uncommon).
Are you sure you want to delete these content types? Are you sure you want to delete these content types?
If you're unsure, answer 'no'. If you're unsure, answer 'no'.
...@@ -191,7 +189,6 @@ If you're unsure, answer 'no'. ...@@ -191,7 +189,6 @@ If you're unsure, answer 'no'.
class NoFastDeleteCollector(Collector): class NoFastDeleteCollector(Collector):
def can_fast_delete(self, *args, **kwargs): def can_fast_delete(self, *args, **kwargs):
""" """
We always want to load the objects into memory so that we can display Always load related objects to display them when showing confirmation.
them to the user when asking confirmation.
""" """
return False return False
...@@ -87,10 +87,9 @@ Minor features ...@@ -87,10 +87,9 @@ Minor features
:mod:`django.contrib.contenttypes` :mod:`django.contrib.contenttypes`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* When stale content types are detected during a management command, there is * When stale content types are detected after the ``migrate`` command, there's
now an expansive list of objects that will be deleted. Previously, only now a list of related objects such as ``auth.Permission``\s that will also be
the content type objects themselves were listed, even if there were objects deleted. Previously, only the content types were listed.
with foreign keys towards the content types that would be deleted also.
:mod:`django.contrib.gis` :mod:`django.contrib.gis`
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -131,6 +131,7 @@ class Post(models.Model): ...@@ -131,6 +131,7 @@ class Post(models.Model):
class ModelWithNullFKToSite(models.Model): class ModelWithNullFKToSite(models.Model):
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
site = models.ForeignKey(Site, null=True, on_delete=models.CASCADE) site = models.ForeignKey(Site, null=True, on_delete=models.CASCADE)
post = models.ForeignKey(Post, null=True, on_delete=models.CASCADE)
def __str__(self): def __str__(self):
return self.title return self.title
......
...@@ -389,21 +389,27 @@ class UpdateContentTypesTests(TestCase): ...@@ -389,21 +389,27 @@ class UpdateContentTypesTests(TestCase):
def test_interactive_true_with_dependent_objects(self): def test_interactive_true_with_dependent_objects(self):
""" """
interactive mode of update_contenttypes() (the default) should delete 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") contenttypes_management.input = lambda x: force_str("yes")
with captured_stdout() as stdout: with captured_stdout() as stdout:
contenttypes_management.update_contenttypes(self.app_config) contenttypes_management.update_contenttypes(self.app_config)
self.assertEqual(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertIn("1 object of type contenttypes_tests.post:", stdout.getvalue()) output = stdout.getvalue()
self.assertIn("Deleting stale content type", 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) self.assertEqual(ContentType.objects.count(), self.before_count)
def test_interactive_true_without_dependent_objects(self): def test_interactive_true_without_dependent_objects(self):
""" """
interactive mode of update_contenttypes() (the default) should delete 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") contenttypes_management.input = lambda x: force_str("yes")
with captured_stdout() as stdout: with captured_stdout() as stdout:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment