Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
@carljm carljm authored
View
15 django/db/models/deletion.py
@@ -76,6 +76,12 @@ def __init__(self, using):
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 @@ def add(self, objs, source=None, nullable=False, reverse_dependency=False):
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 @@ def instances_with_model(self):
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
View
4 tests/regressiontests/delete_regress/models.py
@@ -90,4 +90,6 @@ class FooFile(models.Model):
class FooPhoto(models.Model):
my_photo = models.ForeignKey(Photo)
-
+class FooFileProxy(FooFile):
+ class Meta:
+ proxy = True
View
23 tests/regressiontests/delete_regress/tests.py
@@ -8,7 +8,7 @@
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 @@ def test_delete_concrete_parent(self):
# 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)
Please sign in to comment.
Something went wrong with that request. Please try again.