Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #17918 - Handle proxy models correctly when sorting deletions f…

…or 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
  • Loading branch information...
commit ddd53dafb5fa6ba3cd5075c8e7b3214556c73b50 1 parent edcaf8b
Carl Meyer authored
15  django/db/models/deletion.py
@@ -76,6 +76,12 @@ def __init__(self, using):
76 76
         self.data = {}
77 77
         self.batches = {} # {model: {field: set([instances])}}
78 78
         self.field_updates = {} # {model: {(field, value): set([instances])}}
  79
+
  80
+        # Tracks deletion-order dependency for databases without transactions
  81
+        # or ability to defer constraint checks. Only concrete model classes
  82
+        # should be included, as the dependencies exist only between actual
  83
+        # database tables; proxy models are represented here by their concrete
  84
+        # parent.
79 85
         self.dependencies = {} # {model: set([models])}
80 86
 
81 87
     def add(self, objs, source=None, nullable=False, reverse_dependency=False):
@@ -101,7 +107,8 @@ def add(self, objs, source=None, nullable=False, reverse_dependency=False):
101 107
         if source is not None and not nullable:
102 108
             if reverse_dependency:
103 109
                 source, model = model, source
104  
-            self.dependencies.setdefault(source, set()).add(model)
  110
+            self.dependencies.setdefault(
  111
+                source._meta.concrete_model, set()).add(model._meta.concrete_model)
105 112
         return new_objs
106 113
 
107 114
     def add_batch(self, model, field, objs):
@@ -197,15 +204,17 @@ def instances_with_model(self):
197 204
 
198 205
     def sort(self):
199 206
         sorted_models = []
  207
+        concrete_models = set()
200 208
         models = self.data.keys()
201 209
         while len(sorted_models) < len(models):
202 210
             found = False
203 211
             for model in models:
204 212
                 if model in sorted_models:
205 213
                     continue
206  
-                dependencies = self.dependencies.get(model)
207  
-                if not (dependencies and dependencies.difference(sorted_models)):
  214
+                dependencies = self.dependencies.get(model._meta.concrete_model)
  215
+                if not (dependencies and dependencies.difference(concrete_models)):
208 216
                     sorted_models.append(model)
  217
+                    concrete_models.add(model._meta.concrete_model)
209 218
                     found = True
210 219
             if not found:
211 220
                 return
4  tests/regressiontests/delete_regress/models.py
@@ -90,4 +90,6 @@ class FooFile(models.Model):
90 90
 class FooPhoto(models.Model):
91 91
     my_photo = models.ForeignKey(Photo)
92 92
 
93  
-
  93
+class FooFileProxy(FooFile):
  94
+    class Meta:
  95
+        proxy = True
23  tests/regressiontests/delete_regress/tests.py
@@ -8,7 +8,7 @@
8 8
 
9 9
 from .models import (Book, Award, AwardNote, Person, Child, Toy, PlayedWith,
10 10
     PlayedWithNote, Email, Researcher, Food, Eaten, Policy, Version, Location,
11  
-    Item, Image, File, Photo, FooFile, FooImage, FooPhoto)
  11
+    Item, Image, File, Photo, FooFile, FooImage, FooPhoto, FooFileProxy)
12 12
 
13 13
 
14 14
 # Can't run this test under SQLite, because you can't
@@ -237,3 +237,24 @@ def test_delete_concrete_parent(self):
237 237
         # to it.
238 238
         self.assertEqual(len(FooFile.objects.all()), 0)
239 239
         self.assertEqual(len(FooImage.objects.all()), 0)
  240
+
  241
+
  242
+    def test_delete_proxy_pair(self):
  243
+        """
  244
+        If a pair of proxy models are linked by an FK from one concrete parent
  245
+        to the other, deleting one proxy model cascade-deletes the other, and
  246
+        the deletion happens in the right order (not triggering an
  247
+        IntegrityError on databases unable to defer integrity checks).
  248
+
  249
+        Refs #17918.
  250
+
  251
+        """
  252
+        # Create an Image (proxy of File) and FooFileProxy (proxy of FooFile,
  253
+        # which has an FK to File)
  254
+        image = Image.objects.create()
  255
+        as_file = File.objects.get(pk=image.pk)
  256
+        FooFileProxy.objects.create(my_file=as_file)
  257
+
  258
+        Image.objects.all().delete()
  259
+
  260
+        self.assertEqual(len(FooFileProxy.objects.all()), 0)

0 notes on commit ddd53da

Please sign in to comment.
Something went wrong with that request. Please try again.