[1.7.x] Fixed #22998 -- Updated the fast_delete logic for GFKs
Backport of 6e2b82fdf6
from master
This commit is contained in:
parent
d6f293ad1b
commit
72419ca8da
|
@ -138,9 +138,9 @@ class Collector(object):
|
||||||
include_hidden=True, include_proxy_eq=True):
|
include_hidden=True, include_proxy_eq=True):
|
||||||
if related.field.rel.on_delete is not DO_NOTHING:
|
if related.field.rel.on_delete is not DO_NOTHING:
|
||||||
return False
|
return False
|
||||||
# GFK deletes
|
for field in model._meta.virtual_fields:
|
||||||
for relation in opts.many_to_many:
|
if hasattr(field, 'bulk_related_objects'):
|
||||||
if not relation.rel.through:
|
# It's something like generic foreign key.
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import (
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
from django.db.models.deletion import ProtectedError
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address',
|
__all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address',
|
||||||
|
@ -192,3 +193,26 @@ class D(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('id',)
|
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)
|
||||||
|
|
|
@ -2,11 +2,14 @@ from django.db.models import Q, Sum
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
from django.test import TestCase, skipIfDBFeature
|
from django.test import TestCase, skipIfDBFeature
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
|
from django.db.models.deletion import ProtectedError
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Address, Place, Restaurant, Link, CharLink, TextLink,
|
Address, Place, Restaurant, Link, CharLink, TextLink,
|
||||||
Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company,
|
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):
|
class GenericRelationTests(TestCase):
|
||||||
|
@ -247,3 +250,13 @@ class GenericRelationTests(TestCase):
|
||||||
form.save()
|
form.save()
|
||||||
links = HasLinkThing._meta.get_field_by_name('links')[0]
|
links = HasLinkThing._meta.get_field_by_name('links')[0]
|
||||||
self.assertEqual(links.save_form_data_calls, 1)
|
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…
Reference in New Issue