|
5 | 5 | from django.test import TestCase
|
6 | 6 | from django.utils import six
|
7 | 7 |
|
8 |
| -from .models import Person, Car |
9 |
| - |
10 |
| - |
11 |
| -# #8285: signals can be any callable |
12 |
| -class PostDeleteHandler(object): |
13 |
| - def __init__(self, data): |
14 |
| - self.data = data |
15 |
| - |
16 |
| - def __call__(self, signal, sender, instance, **kwargs): |
17 |
| - self.data.append( |
18 |
| - (instance, instance.id is None) |
19 |
| - ) |
20 |
| - |
21 |
| - |
22 |
| -class MyReceiver(object): |
23 |
| - def __init__(self, param): |
24 |
| - self.param = param |
25 |
| - self._run = False |
26 |
| - |
27 |
| - def __call__(self, signal, sender, **kwargs): |
28 |
| - self._run = True |
29 |
| - signal.disconnect(receiver=self, sender=sender) |
| 8 | +from .models import Author, Book, Car, Person |
30 | 9 |
|
31 | 10 |
|
32 | 11 | class SignalTests(TestCase):
|
33 |
| - def test_basic(self): |
| 12 | + |
| 13 | + def setUp(self): |
34 | 14 | # Save up the number of connected signals so that we can check at the
|
35 | 15 | # end that all the signals we register get properly unregistered (#9989)
|
36 |
| - pre_signals = ( |
| 16 | + self.pre_signals = ( |
37 | 17 | len(signals.pre_save.receivers),
|
38 | 18 | len(signals.post_save.receivers),
|
39 | 19 | len(signals.pre_delete.receivers),
|
40 | 20 | len(signals.post_delete.receivers),
|
41 | 21 | )
|
42 | 22 |
|
| 23 | + def tearDown(self): |
| 24 | + # Check that all our signals got disconnected properly. |
| 25 | + post_signals = ( |
| 26 | + len(signals.pre_save.receivers), |
| 27 | + len(signals.post_save.receivers), |
| 28 | + len(signals.pre_delete.receivers), |
| 29 | + len(signals.post_delete.receivers), |
| 30 | + ) |
| 31 | + self.assertEqual(self.pre_signals, post_signals) |
| 32 | + |
| 33 | + def test_save_signals(self): |
43 | 34 | data = []
|
44 | 35 |
|
45 |
| - def pre_save_test(signal, sender, instance, **kwargs): |
| 36 | + def pre_save_handler(signal, sender, instance, **kwargs): |
46 | 37 | data.append(
|
47 | 38 | (instance, kwargs.get("raw", False))
|
48 | 39 | )
|
49 |
| - signals.pre_save.connect(pre_save_test) |
50 | 40 |
|
51 |
| - def post_save_test(signal, sender, instance, **kwargs): |
| 41 | + def post_save_handler(signal, sender, instance, **kwargs): |
52 | 42 | data.append(
|
53 | 43 | (instance, kwargs.get("created"), kwargs.get("raw", False))
|
54 | 44 | )
|
55 |
| - signals.post_save.connect(post_save_test) |
56 | 45 |
|
57 |
| - def pre_delete_test(signal, sender, instance, **kwargs): |
| 46 | + signals.pre_save.connect(pre_save_handler) |
| 47 | + signals.post_save.connect(post_save_handler) |
| 48 | + try: |
| 49 | + p1 = Person.objects.create(first_name="John", last_name="Smith") |
| 50 | + |
| 51 | + self.assertEqual(data, [ |
| 52 | + (p1, False), |
| 53 | + (p1, True, False), |
| 54 | + ]) |
| 55 | + data[:] = [] |
| 56 | + |
| 57 | + p1.first_name = "Tom" |
| 58 | + p1.save() |
| 59 | + self.assertEqual(data, [ |
| 60 | + (p1, False), |
| 61 | + (p1, False, False), |
| 62 | + ]) |
| 63 | + data[:] = [] |
| 64 | + |
| 65 | + # Calling an internal method purely so that we can trigger a "raw" save. |
| 66 | + p1.save_base(raw=True) |
| 67 | + self.assertEqual(data, [ |
| 68 | + (p1, True), |
| 69 | + (p1, False, True), |
| 70 | + ]) |
| 71 | + data[:] = [] |
| 72 | + |
| 73 | + p2 = Person(first_name="James", last_name="Jones") |
| 74 | + p2.id = 99999 |
| 75 | + p2.save() |
| 76 | + self.assertEqual(data, [ |
| 77 | + (p2, False), |
| 78 | + (p2, True, False), |
| 79 | + ]) |
| 80 | + data[:] = [] |
| 81 | + p2.id = 99998 |
| 82 | + p2.save() |
| 83 | + self.assertEqual(data, [ |
| 84 | + (p2, False), |
| 85 | + (p2, True, False), |
| 86 | + ]) |
| 87 | + finally: |
| 88 | + signals.post_delete.disconnect(pre_save_handler) |
| 89 | + signals.pre_delete.disconnect(post_save_handler) |
| 90 | + |
| 91 | + def test_delete_signals(self): |
| 92 | + data = [] |
| 93 | + |
| 94 | + def pre_delete_handler(signal, sender, instance, **kwargs): |
58 | 95 | data.append(
|
59 | 96 | (instance, instance.id is None)
|
60 | 97 | )
|
61 |
| - signals.pre_delete.connect(pre_delete_test) |
62 | 98 |
|
63 |
| - post_delete_test = PostDeleteHandler(data) |
64 |
| - signals.post_delete.connect(post_delete_test) |
| 99 | + # #8285: signals can be any callable |
| 100 | + class PostDeleteHandler(object): |
| 101 | + def __init__(self, data): |
| 102 | + self.data = data |
| 103 | + |
| 104 | + def __call__(self, signal, sender, instance, **kwargs): |
| 105 | + self.data.append( |
| 106 | + (instance, instance.id is None) |
| 107 | + ) |
| 108 | + post_delete_handler = PostDeleteHandler(data) |
| 109 | + |
| 110 | + signals.pre_delete.connect(pre_delete_handler) |
| 111 | + signals.post_delete.connect(post_delete_handler) |
| 112 | + try: |
| 113 | + p1 = Person.objects.create(first_name="John", last_name="Smith") |
| 114 | + p1.delete() |
| 115 | + self.assertEqual(data, [ |
| 116 | + (p1, False), |
| 117 | + (p1, False), |
| 118 | + ]) |
| 119 | + data[:] = [] |
| 120 | + |
| 121 | + p2 = Person(first_name="James", last_name="Jones") |
| 122 | + p2.id = 99999 |
| 123 | + p2.save() |
| 124 | + p2.id = 99998 |
| 125 | + p2.save() |
| 126 | + p2.delete() |
| 127 | + self.assertEqual(data, [ |
| 128 | + (p2, False), |
| 129 | + (p2, False) |
| 130 | + ]) |
| 131 | + data[:] = [] |
| 132 | + |
| 133 | + self.assertQuerysetEqual( |
| 134 | + Person.objects.all(), [ |
| 135 | + "James Jones", |
| 136 | + ], |
| 137 | + six.text_type |
| 138 | + ) |
| 139 | + finally: |
| 140 | + signals.post_delete.disconnect(pre_delete_handler) |
| 141 | + signals.pre_delete.disconnect(post_delete_handler) |
| 142 | + |
| 143 | + def test_decorators(self): |
| 144 | + data = [] |
65 | 145 |
|
66 |
| - # throw a decorator syntax receiver into the mix |
67 | 146 | @receiver(signals.pre_save)
|
68 |
| - def pre_save_decorator_test(signal, sender, instance, **kwargs): |
| 147 | + def decorated_handler(signal, sender, instance, **kwargs): |
69 | 148 | data.append(instance)
|
70 | 149 |
|
71 | 150 | @receiver(signals.pre_save, sender=Car)
|
72 |
| - def pre_save_decorator_sender_test(signal, sender, instance, **kwargs): |
| 151 | + def decorated_handler_with_sender_arg(signal, sender, instance, **kwargs): |
73 | 152 | data.append(instance)
|
74 | 153 |
|
75 |
| - p1 = Person(first_name="John", last_name="Smith") |
76 |
| - self.assertEqual(data, []) |
77 |
| - p1.save() |
78 |
| - self.assertEqual(data, [ |
79 |
| - (p1, False), |
80 |
| - p1, |
81 |
| - (p1, True, False), |
82 |
| - ]) |
83 |
| - data[:] = [] |
84 |
| - |
85 |
| - p1.first_name = "Tom" |
86 |
| - p1.save() |
87 |
| - self.assertEqual(data, [ |
88 |
| - (p1, False), |
89 |
| - p1, |
90 |
| - (p1, False, False), |
91 |
| - ]) |
92 |
| - data[:] = [] |
93 |
| - |
94 |
| - # Car signal (sender defined) |
95 |
| - c1 = Car(make="Volkswagon", model="Passat") |
96 |
| - c1.save() |
97 |
| - self.assertEqual(data, [ |
98 |
| - (c1, False), |
99 |
| - c1, |
100 |
| - c1, |
101 |
| - (c1, True, False), |
102 |
| - ]) |
103 |
| - data[:] = [] |
104 |
| - |
105 |
| - # Calling an internal method purely so that we can trigger a "raw" save. |
106 |
| - p1.save_base(raw=True) |
107 |
| - self.assertEqual(data, [ |
108 |
| - (p1, True), |
109 |
| - p1, |
110 |
| - (p1, False, True), |
111 |
| - ]) |
112 |
| - data[:] = [] |
113 |
| - |
114 |
| - p1.delete() |
115 |
| - self.assertEqual(data, [ |
116 |
| - (p1, False), |
117 |
| - (p1, False), |
118 |
| - ]) |
119 |
| - data[:] = [] |
120 |
| - |
121 |
| - p2 = Person(first_name="James", last_name="Jones") |
122 |
| - p2.id = 99999 |
123 |
| - p2.save() |
124 |
| - self.assertEqual(data, [ |
125 |
| - (p2, False), |
126 |
| - p2, |
127 |
| - (p2, True, False), |
128 |
| - ]) |
129 |
| - data[:] = [] |
130 |
| - |
131 |
| - p2.id = 99998 |
132 |
| - p2.save() |
133 |
| - self.assertEqual(data, [ |
134 |
| - (p2, False), |
135 |
| - p2, |
136 |
| - (p2, True, False), |
137 |
| - ]) |
138 |
| - data[:] = [] |
139 |
| - |
140 |
| - p2.delete() |
141 |
| - self.assertEqual(data, [ |
142 |
| - (p2, False), |
143 |
| - (p2, False) |
144 |
| - ]) |
145 |
| - |
146 |
| - self.assertQuerysetEqual( |
147 |
| - Person.objects.all(), [ |
148 |
| - "James Jones", |
149 |
| - ], |
150 |
| - six.text_type |
151 |
| - ) |
| 154 | + try: |
| 155 | + c1 = Car.objects.create(make="Volkswagon", model="Passat") |
| 156 | + self.assertEqual(data, [c1, c1]) |
| 157 | + finally: |
| 158 | + signals.post_delete.disconnect(decorated_handler) |
| 159 | + signals.pre_delete.disconnect(decorated_handler_with_sender_arg, sender=Car) |
152 | 160 |
|
153 |
| - signals.post_delete.disconnect(post_delete_test) |
154 |
| - signals.pre_delete.disconnect(pre_delete_test) |
155 |
| - signals.post_save.disconnect(post_save_test) |
156 |
| - signals.pre_save.disconnect(pre_save_test) |
157 |
| - signals.pre_save.disconnect(pre_save_decorator_test) |
158 |
| - signals.pre_save.disconnect(pre_save_decorator_sender_test, sender=Car) |
| 161 | + def test_save_and_delete_signals_with_m2m(self): |
| 162 | + data = [] |
159 | 163 |
|
160 |
| - # Check that all our signals got disconnected properly. |
161 |
| - post_signals = ( |
162 |
| - len(signals.pre_save.receivers), |
163 |
| - len(signals.post_save.receivers), |
164 |
| - len(signals.pre_delete.receivers), |
165 |
| - len(signals.post_delete.receivers), |
166 |
| - ) |
167 |
| - self.assertEqual(pre_signals, post_signals) |
| 164 | + def pre_save_handler(signal, sender, instance, **kwargs): |
| 165 | + data.append('pre_save signal, %s' % instance) |
| 166 | + if kwargs.get('raw'): |
| 167 | + data.append('Is raw') |
| 168 | + |
| 169 | + def post_save_handler(signal, sender, instance, **kwargs): |
| 170 | + data.append('post_save signal, %s' % instance) |
| 171 | + if 'created' in kwargs: |
| 172 | + if kwargs['created']: |
| 173 | + data.append('Is created') |
| 174 | + else: |
| 175 | + data.append('Is updated') |
| 176 | + if kwargs.get('raw'): |
| 177 | + data.append('Is raw') |
| 178 | + |
| 179 | + def pre_delete_handler(signal, sender, instance, **kwargs): |
| 180 | + data.append('pre_save signal, %s' % instance) |
| 181 | + data.append('instance.id is not None: %s' % (instance.id is not None)) |
| 182 | + |
| 183 | + def post_delete_handler(signal, sender, instance, **kwargs): |
| 184 | + data.append('post_delete signal, %s' % instance) |
| 185 | + data.append('instance.id is not None: %s' % (instance.id is not None)) |
| 186 | + |
| 187 | + signals.pre_save.connect(pre_save_handler) |
| 188 | + signals.post_save.connect(post_save_handler) |
| 189 | + signals.pre_delete.connect(pre_delete_handler) |
| 190 | + signals.post_delete.connect(post_delete_handler) |
| 191 | + try: |
| 192 | + a1 = Author.objects.create(name='Neal Stephenson') |
| 193 | + self.assertEqual(data, [ |
| 194 | + "pre_save signal, Neal Stephenson", |
| 195 | + "post_save signal, Neal Stephenson", |
| 196 | + "Is created" |
| 197 | + ]) |
| 198 | + data[:] = [] |
| 199 | + |
| 200 | + b1 = Book.objects.create(name='Snow Crash') |
| 201 | + self.assertEqual(data, [ |
| 202 | + "pre_save signal, Snow Crash", |
| 203 | + "post_save signal, Snow Crash", |
| 204 | + "Is created" |
| 205 | + ]) |
| 206 | + data[:] = [] |
| 207 | + |
| 208 | + # Assigning and removing to/from m2m shouldn't generate an m2m signal. |
| 209 | + b1.authors = [a1] |
| 210 | + self.assertEqual(data, []) |
| 211 | + b1.authors = [] |
| 212 | + self.assertEqual(data, []) |
| 213 | + finally: |
| 214 | + signals.post_delete.disconnect(pre_save_handler) |
| 215 | + signals.pre_delete.disconnect(post_save_handler) |
| 216 | + signals.post_save.disconnect(pre_delete_handler) |
| 217 | + signals.pre_save.disconnect(post_delete_handler) |
168 | 218 |
|
169 | 219 | def test_disconnect_in_dispatch(self):
|
170 | 220 | """
|
171 | 221 | Test that signals that disconnect when being called don't mess future
|
172 | 222 | dispatching.
|
173 | 223 | """
|
174 |
| - a, b = MyReceiver(1), MyReceiver(2) |
| 224 | + |
| 225 | + class Handler(object): |
| 226 | + def __init__(self, param): |
| 227 | + self.param = param |
| 228 | + self._run = False |
| 229 | + |
| 230 | + def __call__(self, signal, sender, **kwargs): |
| 231 | + self._run = True |
| 232 | + signal.disconnect(receiver=self, sender=sender) |
| 233 | + |
| 234 | + a, b = Handler(1), Handler(2) |
175 | 235 | signals.post_save.connect(sender=Person, receiver=a)
|
176 | 236 | signals.post_save.connect(sender=Person, receiver=b)
|
177 | 237 | Person.objects.create(first_name='John', last_name='Smith')
|
|
0 commit comments