Loading django/db/models/deletion.py +3 −3 Original line number Diff line number Diff line Loading @@ -138,9 +138,9 @@ class Collector(object): include_hidden=True, include_proxy_eq=True): if related.field.rel.on_delete is not DO_NOTHING: return False # GFK deletes for relation in opts.many_to_many: if not relation.rel.through: for field in model._meta.virtual_fields: if hasattr(field, 'bulk_related_objects'): # It's something like generic foreign key. return False return True Loading tests/generic_relations_regress/models.py +24 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import ( from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.db.models.deletion import ProtectedError __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', Loading Loading @@ -192,3 +193,26 @@ class D(models.Model): class Meta: ordering = ('id',) # Ticket #22998 class Node(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content = GenericForeignKey('content_type', 'object_id') class Content(models.Model): nodes = GenericRelation(Node) related_obj = models.ForeignKey('Related', on_delete=models.CASCADE) class Related(models.Model): pass def prevent_deletes(sender, instance, **kwargs): raise ProtectedError("Not allowed to delete.", [instance]) models.signals.pre_delete.connect(prevent_deletes, sender=Node) tests/generic_relations_regress/tests.py +14 −1 Original line number Diff line number Diff line Loading @@ -2,11 +2,14 @@ from django.db.models import Q, Sum from django.db.utils import IntegrityError from django.test import TestCase, skipIfDBFeature from django.forms.models import modelform_factory from django.db.models.deletion import ProtectedError from .models import ( Address, Place, Restaurant, Link, CharLink, TextLink, Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company, Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D) Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D, Related, Content, Node, ) class GenericRelationTests(TestCase): Loading Loading @@ -247,3 +250,13 @@ class GenericRelationTests(TestCase): form.save() links = HasLinkThing._meta.get_field_by_name('links')[0] self.assertEqual(links.save_form_data_calls, 1) def test_ticket_22998(self): related = Related.objects.create() content = Content.objects.create(related_obj=related) node = Node.objects.create(content=content) # deleting the Related cascades to the Content cascades to the Node, # where the pre_delete signal should fire and prevent deletion. with self.assertRaises(ProtectedError): related.delete() Loading
django/db/models/deletion.py +3 −3 Original line number Diff line number Diff line Loading @@ -138,9 +138,9 @@ class Collector(object): include_hidden=True, include_proxy_eq=True): if related.field.rel.on_delete is not DO_NOTHING: return False # GFK deletes for relation in opts.many_to_many: if not relation.rel.through: for field in model._meta.virtual_fields: if hasattr(field, 'bulk_related_objects'): # It's something like generic foreign key. return False return True Loading
tests/generic_relations_regress/models.py +24 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import ( from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.db.models.deletion import ProtectedError __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', Loading Loading @@ -192,3 +193,26 @@ class D(models.Model): class Meta: ordering = ('id',) # Ticket #22998 class Node(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content = GenericForeignKey('content_type', 'object_id') class Content(models.Model): nodes = GenericRelation(Node) related_obj = models.ForeignKey('Related', on_delete=models.CASCADE) class Related(models.Model): pass def prevent_deletes(sender, instance, **kwargs): raise ProtectedError("Not allowed to delete.", [instance]) models.signals.pre_delete.connect(prevent_deletes, sender=Node)
tests/generic_relations_regress/tests.py +14 −1 Original line number Diff line number Diff line Loading @@ -2,11 +2,14 @@ from django.db.models import Q, Sum from django.db.utils import IntegrityError from django.test import TestCase, skipIfDBFeature from django.forms.models import modelform_factory from django.db.models.deletion import ProtectedError from .models import ( Address, Place, Restaurant, Link, CharLink, TextLink, Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company, Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D) Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D, Related, Content, Node, ) class GenericRelationTests(TestCase): Loading Loading @@ -247,3 +250,13 @@ class GenericRelationTests(TestCase): form.save() links = HasLinkThing._meta.get_field_by_name('links')[0] self.assertEqual(links.save_form_data_calls, 1) def test_ticket_22998(self): related = Related.objects.create() content = Content.objects.create(related_obj=related) node = Node.objects.create(content=content) # deleting the Related cascades to the Content cascades to the Node, # where the pre_delete signal should fire and prevent deletion. with self.assertRaises(ProtectedError): related.delete()