Commit ddd53daf authored by Carl Meyer's avatar Carl Meyer
Browse files

Fixed #17918 - Handle proxy models correctly when sorting deletions for...

Fixed #17918 - Handle proxy models correctly when sorting deletions for databases without deferred constraints. Thanks Nate Bragg for the report.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17756 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent edcaf8b7
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -76,6 +76,12 @@ class Collector(object):
        self.data = {}
        self.batches = {} # {model: {field: set([instances])}}
        self.field_updates = {} # {model: {(field, value): set([instances])}}

        # Tracks deletion-order dependency for databases without transactions
        # or ability to defer constraint checks. Only concrete model classes
        # should be included, as the dependencies exist only between actual
        # database tables; proxy models are represented here by their concrete
        # parent.
        self.dependencies = {} # {model: set([models])}

    def add(self, objs, source=None, nullable=False, reverse_dependency=False):
@@ -101,7 +107,8 @@ class Collector(object):
        if source is not None and not nullable:
            if reverse_dependency:
                source, model = model, source
            self.dependencies.setdefault(source, set()).add(model)
            self.dependencies.setdefault(
                source._meta.concrete_model, set()).add(model._meta.concrete_model)
        return new_objs

    def add_batch(self, model, field, objs):
@@ -197,15 +204,17 @@ class Collector(object):

    def sort(self):
        sorted_models = []
        concrete_models = set()
        models = self.data.keys()
        while len(sorted_models) < len(models):
            found = False
            for model in models:
                if model in sorted_models:
                    continue
                dependencies = self.dependencies.get(model)
                if not (dependencies and dependencies.difference(sorted_models)):
                dependencies = self.dependencies.get(model._meta.concrete_model)
                if not (dependencies and dependencies.difference(concrete_models)):
                    sorted_models.append(model)
                    concrete_models.add(model._meta.concrete_model)
                    found = True
            if not found:
                return
+3 −1
Original line number Diff line number Diff line
@@ -90,4 +90,6 @@ class FooFile(models.Model):
class FooPhoto(models.Model):
    my_photo = models.ForeignKey(Photo)

class FooFileProxy(FooFile):
    class Meta:
        proxy = True
+22 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature

from .models import (Book, Award, AwardNote, Person, Child, Toy, PlayedWith,
    PlayedWithNote, Email, Researcher, Food, Eaten, Policy, Version, Location,
    Item, Image, File, Photo, FooFile, FooImage, FooPhoto)
    Item, Image, File, Photo, FooFile, FooImage, FooPhoto, FooFileProxy)


# Can't run this test under SQLite, because you can't
@@ -237,3 +237,24 @@ class ProxyDeleteTest(TestCase):
        # to it.
        self.assertEqual(len(FooFile.objects.all()), 0)
        self.assertEqual(len(FooImage.objects.all()), 0)


    def test_delete_proxy_pair(self):
        """
        If a pair of proxy models are linked by an FK from one concrete parent
        to the other, deleting one proxy model cascade-deletes the other, and
        the deletion happens in the right order (not triggering an
        IntegrityError on databases unable to defer integrity checks).

        Refs #17918.

        """
        # Create an Image (proxy of File) and FooFileProxy (proxy of FooFile,
        # which has an FK to File)
        image = Image.objects.create()
        as_file = File.objects.get(pk=image.pk)
        FooFileProxy.objects.create(my_file=as_file)

        Image.objects.all().delete()

        self.assertEqual(len(FooFileProxy.objects.all()), 0)