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
This commit is contained in:
Luke Plant 2008-06-21 20:55:17 +00:00
parent 17bc2820bb
commit 7c621535a2
3 changed files with 110 additions and 5 deletions

View File

@ -2,6 +2,8 @@
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.datastructures import SortedDict
import sys import sys
import os import os
import threading import threading
@ -18,10 +20,10 @@ class AppCache(object):
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531. # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
__shared_state = dict( __shared_state = dict(
# Keys of app_store are the model modules for each application. # 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. # 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. # Mapping of app_labels to errors raised when trying to import the app.
app_errors = {}, app_errors = {},
@ -133,7 +135,7 @@ class AppCache(object):
""" """
self._populate() self._populate()
if app_mod: 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: else:
model_list = [] model_list = []
for app_entry in self.app_models.itervalues(): for app_entry in self.app_models.itervalues():
@ -149,7 +151,7 @@ class AppCache(object):
""" """
if seed_cache: if seed_cache:
self._populate() 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): def register_models(self, app_label, *models):
""" """
@ -159,7 +161,7 @@ class AppCache(object):
# Store as 'name: model' pair in a dictionary # Store as 'name: model' pair in a dictionary
# in the app_models dictionary # in the app_models dictionary
model_name = model._meta.object_name.lower() 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: if model_name in model_dict:
# The same model may be imported via different paths (e.g. # The same model may be imported via different paths (e.g.
# appname.models and project.appname.models). We use the source # appname.models and project.appname.models). We use the source

View File

@ -0,0 +1 @@

View File

@ -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()
"""
}