Browse files

Added tests for corner case with deleting where objects are deleted i…

…n 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
  • Loading branch information...
1 parent 17bc282 commit 7c621535a2b7a7cc58ed3658ab1f5a74d03abca3 @spookylukey spookylukey committed Jun 21, 2008
Showing with 110 additions and 5 deletions.
  1. +7 −5 django/db/models/loading.py
  2. +1 −0 tests/modeltests/delete/__init__.py
  3. +102 −0 tests/modeltests/delete/models.py
View
12 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 @@ def get_models(self, app_mod=None):
"""
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 @@ def get_model(self, app_label, model_name, seed_cache=True):
"""
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 @@ def register_models(self, app_label, *models):
# 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
View
1 tests/modeltests/delete/__init__.py
@@ -0,0 +1 @@
+
View
102 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()
+
+
+
+"""
+}

0 comments on commit 7c62153

Please sign in to comment.