Commit 227a0f27 authored by Gavin Wahl's avatar Gavin Wahl Committed by Anssi Kääriäinen
Browse files

[1.6.x] Fixed #22998 -- Updated the fast_delete logic for GFKs

Backport of 6e2b82fd from master
parent 609153d2
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -136,9 +136,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

+24 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from django.contrib.contenttypes import generic
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',
@@ -164,3 +165,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 = generic.GenericForeignKey('content_type', 'object_id')


class Content(models.Model):
    nodes = generic.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)
+14 −1
Original line number Diff line number Diff line
@@ -2,11 +2,14 @@ from django.db.models import Q
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):
@@ -223,3 +226,13 @@ class GenericRelationTests(TestCase):
        form.save()
        links = HasLinkThing._meta.get_field_by_name('links')[0].field
        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()