summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Plant <L.Plant.98@cantab.net>2008-06-21 20:55:17 +0000
committerLuke Plant <L.Plant.98@cantab.net>2008-06-21 20:55:17 +0000
commit7c621535a2b7a7cc58ed3658ab1f5a74d03abca3 (patch)
tree52383030f17b53ded07f032085c329964d17f632
parent17bc2820bbc95e01d8365a8fc6fcb497852e5f23 (diff)
Added tests for corner case with deleting where objects are deleted in the wrong order.
These tests currently fail, by design, fix will be committed shortly. git-svn-id: http://code.djangoproject.com/svn/django/trunk@7721 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/models/loading.py12
-rw-r--r--tests/modeltests/delete/__init__.py1
-rw-r--r--tests/modeltests/delete/models.py102
3 files changed, 110 insertions, 5 deletions
diff --git a/django/db/models/loading.py b/django/db/models/loading.py
index e62188adf7..6837e070ac 100644
--- a/django/db/models/loading.py
+++ b/django/db/models/loading.py
@@ -2,6 +2,8 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
+from django.utils.datastructures import SortedDict
+
import sys
import os
import threading
@@ -18,10 +20,10 @@ class AppCache(object):
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
__shared_state = dict(
# Keys of app_store are the model modules for each application.
- app_store = {},
+ app_store = SortedDict(),
# Mapping of app_labels to a dictionary of model names to model code.
- app_models = {},
+ app_models = SortedDict(),
# Mapping of app_labels to errors raised when trying to import the app.
app_errors = {},
@@ -133,7 +135,7 @@ class AppCache(object):
"""
self._populate()
if app_mod:
- return self.app_models.get(app_mod.__name__.split('.')[-2], {}).values()
+ return self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict()).values()
else:
model_list = []
for app_entry in self.app_models.itervalues():
@@ -149,7 +151,7 @@ class AppCache(object):
"""
if seed_cache:
self._populate()
- return self.app_models.get(app_label, {}).get(model_name.lower())
+ return self.app_models.get(app_label, SortedDict()).get(model_name.lower())
def register_models(self, app_label, *models):
"""
@@ -159,7 +161,7 @@ class AppCache(object):
# Store as 'name: model' pair in a dictionary
# in the app_models dictionary
model_name = model._meta.object_name.lower()
- model_dict = self.app_models.setdefault(app_label, {})
+ model_dict = self.app_models.setdefault(app_label, SortedDict())
if model_name in model_dict:
# The same model may be imported via different paths (e.g.
# appname.models and project.appname.models). We use the source
diff --git a/tests/modeltests/delete/__init__.py b/tests/modeltests/delete/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tests/modeltests/delete/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py
new file mode 100644
index 0000000000..d4f7dd1d1f
--- /dev/null
+++ b/tests/modeltests/delete/models.py
@@ -0,0 +1,102 @@
+# coding: utf-8
+"""
+Tests for some corner cases with deleting.
+"""
+
+from django.db import models
+
+class DefaultRepr(object):
+ def __repr__(self):
+ return u"<%s: %s>" % (self.__class__.__name__, self.__dict__)
+
+class A(DefaultRepr, models.Model):
+ pass
+
+class B(DefaultRepr, models.Model):
+ a = models.ForeignKey(A)
+
+class C(DefaultRepr, models.Model):
+ b = models.ForeignKey(B)
+
+class D(DefaultRepr, models.Model):
+ c = models.ForeignKey(C)
+ a = models.ForeignKey(A)
+
+# Simplified, we have:
+# A
+# B -> A
+# C -> B
+# D -> C
+# D -> A
+
+# So, we must delete Ds first of all, then Cs then Bs then As.
+# However, if we start at As, we might find Bs first (in which
+# case things will be nice), or find Ds first.
+
+
+__test__ = {'API_TESTS': """
+# Due to the way that transactions work in the test harness,
+# doing m.delete() here can work but fail in a real situation,
+# since it may delete all objects, but not in the right order.
+# So we manually check that the order of deletion is correct.
+
+# Also, it is possible that the order is correct 'accidentally', due
+# solely to order of imports etc. To check this, we set the order
+# that 'get_models()' will retrieve to a known 'tricky' order, and
+# then try again with the reverse and try again. Slightly naughty
+# access to internals here.
+
+>>> from django.utils.datastructures import SortedDict
+>>> from django.db.models.loading import cache
+
+# Nice order
+>>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd']
+>>> del A._meta._related_objects_cache
+>>> del B._meta._related_objects_cache
+>>> del C._meta._related_objects_cache
+>>> del D._meta._related_objects_cache
+
+
+
+>>> a1 = A()
+>>> a1.save()
+>>> b1 = B(a=a1)
+>>> b1.save()
+>>> c1 = C(b=b1)
+>>> c1.save()
+>>> d1 = D(c=c1, a=a1)
+>>> d1.save()
+
+>>> sd = SortedDict()
+>>> a1._collect_sub_objects(sd)
+>>> list(reversed(sd.keys()))
+[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
+>>> a1.delete()
+
+# Same again with a known bad order
+>>> cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a']
+>>> del A._meta._related_objects_cache
+>>> del B._meta._related_objects_cache
+>>> del C._meta._related_objects_cache
+>>> del D._meta._related_objects_cache
+
+
+>>> a2 = A()
+>>> a2.save()
+>>> b2 = B(a=a2)
+>>> b2.save()
+>>> c2 = C(b=b2)
+>>> c2.save()
+>>> d2 = D(c=c2, a=a2)
+>>> d2.save()
+
+>>> sd2 = SortedDict()
+>>> a2._collect_sub_objects(sd2)
+>>> list(reversed(sd2.keys()))
+[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
+>>> a2.delete()
+
+
+
+"""
+}