diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index c5febce623a..a8c98f5b83f 100644 --- a/django/db/models/deletion.py +++ b/django/db/models/deletion.py @@ -231,12 +231,9 @@ class Collector(object): field.remote_field.on_delete(self, field, sub_objs, self.using) for field in model._meta.virtual_fields: if hasattr(field, 'bulk_related_objects'): - # Its something like generic foreign key. + # It's something like generic foreign key. sub_objs = field.bulk_related_objects(new_objs, self.using) - self.collect(sub_objs, - source=model, - source_attr=field.remote_field.related_name, - nullable=True) + self.collect(sub_objs, source=model, nullable=True) def related_objects(self, related, objs): """ diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index b13bd32115f..c3cedf84443 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -24,9 +24,9 @@ from django.utils.six import StringIO from .models import ( Actor, AdminOrderedAdminMethod, AdminOrderedCallable, AdminOrderedField, AdminOrderedModelMethod, Album, Answer, Article, BarAccount, Book, - Category, Chapter, ChapterXtra1, Child, ChildOfReferer, Choice, City, - Collector, Color, Color2, ComplexSortedPerson, CoverLetter, CustomArticle, - CyclicOne, CyclicTwo, DependentChild, DooHickey, EmptyModel, + Bookmark, Category, Chapter, ChapterXtra1, Child, ChildOfReferer, Choice, + City, Collector, Color, Color2, ComplexSortedPerson, CoverLetter, + CustomArticle, CyclicOne, CyclicTwo, DependentChild, DooHickey, EmptyModel, EmptyModelHidden, EmptyModelMixin, EmptyModelVisible, ExplicitlyProvidedPK, ExternalSubscriber, Fabric, FancyDoodad, FieldOverridePost, FilteredManager, FooAccount, FoodDelivery, FunkyTag, Gadget, Gallery, @@ -912,6 +912,7 @@ site.register(Villain) site.register(SuperVillain) site.register(Plot) site.register(PlotDetails) +site.register(Bookmark) site.register(CyclicOne) site.register(CyclicTwo) site.register(WorkHour, WorkHourAdmin) diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index fd1b8b33a32..1199da3973f 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -556,6 +556,15 @@ class SuperSecretHideout(models.Model): return self.location +@python_2_unicode_compatible +class Bookmark(models.Model): + name = models.CharField(max_length=60) + tag = GenericRelation(FunkyTag, related_query_name='bookmark') + + def __str__(self): + return self.name + + @python_2_unicode_compatible class CyclicOne(models.Model): name = models.CharField(max_length=25) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 79db4a3712e..f10075d49f8 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -43,21 +43,21 @@ from . import customadmin from .admin import CityAdmin, site, site2 from .models import ( Actor, AdminOrderedAdminMethod, AdminOrderedCallable, AdminOrderedField, - AdminOrderedModelMethod, Answer, Article, BarAccount, Book, Category, - Chapter, ChapterXtra1, ChapterXtra2, Character, Child, Choice, City, - Collector, Color, Color2, ComplexSortedPerson, CoverLetter, CustomArticle, - CyclicOne, CyclicTwo, DooHickey, Employee, EmptyModel, ExternalSubscriber, - Fabric, FancyDoodad, FieldOverridePost, FilteredManager, FooAccount, - FoodDelivery, FunkyTag, Gallery, Grommet, Inquisition, Language, - MainPrepopulated, ModelWithStringPrimaryKey, OtherStory, Paper, Parent, - ParentWithDependentChildren, Person, Persona, Picture, Pizza, Plot, - PlotDetails, PluggableSearchPerson, Podcast, Post, PrePopulatedPost, Promo, - Question, Recommendation, Recommender, RelatedPrepopulated, Report, - Restaurant, RowLevelChangePermissionModel, SecretHideout, Section, - ShortMessage, Simple, State, Story, Subscriber, SuperSecretHideout, - SuperVillain, Telegram, TitleTranslation, Topping, UnchangeableObject, - UndeletableObject, UnorderedObject, Villain, Vodcast, Whatsit, Widget, - Worker, WorkHour, + AdminOrderedModelMethod, Answer, Article, BarAccount, Book, Bookmark, + Category, Chapter, ChapterXtra1, ChapterXtra2, Character, Child, Choice, + City, Collector, Color, Color2, ComplexSortedPerson, CoverLetter, + CustomArticle, CyclicOne, CyclicTwo, DooHickey, Employee, EmptyModel, + ExternalSubscriber, Fabric, FancyDoodad, FieldOverridePost, + FilteredManager, FooAccount, FoodDelivery, FunkyTag, Gallery, Grommet, + Inquisition, Language, MainPrepopulated, ModelWithStringPrimaryKey, + OtherStory, Paper, Parent, ParentWithDependentChildren, Person, Persona, + Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, Podcast, Post, + PrePopulatedPost, Promo, Question, Recommendation, Recommender, + RelatedPrepopulated, Report, Restaurant, RowLevelChangePermissionModel, + SecretHideout, Section, ShortMessage, Simple, State, Story, Subscriber, + SuperSecretHideout, SuperVillain, Telegram, TitleTranslation, Topping, + UnchangeableObject, UndeletableObject, UnorderedObject, Villain, Vodcast, + Whatsit, Widget, Worker, WorkHour, ) @@ -2232,6 +2232,19 @@ class AdminViewDeletedObjectsTest(TestCase): response = self.client.get(reverse('admin:admin_views_plot_delete', args=(plot.pk,))) self.assertContains(response, should_contain) + def test_generic_relations_with_related_query_name(self): + """ + If a deleted object has GenericForeignKey with + GenericRelation(related_query_name='...') pointing to it, those objects + should be listed for deletion. + """ + bookmark = Bookmark.objects.create(name='djangoproject') + tag = FunkyTag.objects.create(content_object=bookmark, name='django') + tag_url = reverse('admin:admin_views_funkytag_change', args=(tag.id,)) + should_contain = '