3
Tests for some corner cases with deleting.
6
from django.db import models
8
class DefaultRepr(object):
10
return u"<%s: %s>" % (self.__class__.__name__, self.__dict__)
12
class A(DefaultRepr, models.Model):
15
class B(DefaultRepr, models.Model):
16
a = models.ForeignKey(A)
18
class C(DefaultRepr, models.Model):
19
b = models.ForeignKey(B)
21
class D(DefaultRepr, models.Model):
22
c = models.ForeignKey(C)
23
a = models.ForeignKey(A)
25
# Simplified, we have:
32
# So, we must delete Ds first of all, then Cs then Bs then As.
33
# However, if we start at As, we might find Bs first (in which
34
# case things will be nice), or find Ds first.
36
# Some mutually dependent models, but nullable
37
class E(DefaultRepr, models.Model):
38
f = models.ForeignKey('F', null=True, related_name='e_rel')
40
class F(DefaultRepr, models.Model):
41
e = models.ForeignKey(E, related_name='f_rel')
44
__test__ = {'API_TESTS': """
45
### Tests for models A,B,C,D ###
47
## First, test the CollectedObjects data structure directly
49
>>> from django.db.models.query import CollectedObjects
51
>>> g = CollectedObjects()
52
>>> g.add("key1", 1, "item1", None)
56
>>> g.add("key2", 1, "item1", "key1")
58
>>> g.add("key2", 2, "item2", "key1")
61
{1: 'item1', 2: 'item2'}
62
>>> g.add("key3", 1, "item1", "key1")
64
>>> g.add("key3", 1, "item1", "key2")
67
['key3', 'key2', 'key1']
69
>>> g.add("key2", 1, "item1", "key3")
72
Traceback (most recent call last):
74
CyclicDependency: There is a cyclic dependency of items to be processed.
77
## Second, test the usage of CollectedObjects by Model.delete()
79
# Due to the way that transactions work in the test harness,
80
# doing m.delete() here can work but fail in a real situation,
81
# since it may delete all objects, but not in the right order.
82
# So we manually check that the order of deletion is correct.
84
# Also, it is possible that the order is correct 'accidentally', due
85
# solely to order of imports etc. To check this, we set the order
86
# that 'get_models()' will retrieve to a known 'nice' order, and
87
# then try again with a known 'tricky' order. Slightly naughty
88
# access to internals here :-)
90
# If implementation changes, then the tests may need to be simplified:
91
# - remove the lines that set the .keyOrder and clear the related
93
# - remove the second set of tests (with a2, b2 etc)
95
>>> from django.db.models.loading import cache
97
>>> def clear_rel_obj_caches(models):
99
... if hasattr(m._meta, '_related_objects_cache'):
100
... del m._meta._related_objects_cache
103
>>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd']
104
>>> clear_rel_obj_caches([A, B, C, D])
112
>>> d1 = D(c=c1, a=a1)
115
>>> o = CollectedObjects()
116
>>> a1._collect_sub_objects(o)
118
[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
121
# Same again with a known bad order
122
>>> cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a']
123
>>> clear_rel_obj_caches([A, B, C, D])
131
>>> d2 = D(c=c2, a=a2)
134
>>> o = CollectedObjects()
135
>>> a2._collect_sub_objects(o)
137
[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
140
### Tests for models E,F - nullable related fields ###
142
## First, test the CollectedObjects data structure directly
144
>>> g = CollectedObjects()
145
>>> g.add("key1", 1, "item1", None)
147
>>> g.add("key2", 1, "item1", "key1", nullable=True)
149
>>> g.add("key1", 1, "item1", "key2")
154
## Second, test the usage of CollectedObjects by Model.delete()
163
# Since E.f is nullable, we should delete F first (after nulling out
164
# the E.f field), then E.
166
>>> o = CollectedObjects()
167
>>> e1._collect_sub_objects(o)
169
[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]
180
# Same deal as before, though we are starting from the other object.
182
>>> o = CollectedObjects()
183
>>> f2._collect_sub_objects(o)
185
[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]