mirror of https://github.com/django/django.git
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:
parent
17bc2820bb
commit
7c621535a2
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
"""
|
||||
}
|
Loading…
Reference in New Issue