~ubuntu-branches/ubuntu/oneiric/python-django/oneiric

« back to all changes in this revision

Viewing changes to tests/modeltests/m2m_signals/models.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2010-05-21 07:52:55 UTC
  • mfrom: (1.1.10 upstream) (4.4.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100521075255-i1zpeyc0k8512pd7
Tags: 1.2-1
New upstream stable release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Testing signals emitted on changing m2m relations.
 
3
"""
 
4
 
 
5
from django.db import models
 
6
 
 
7
class Part(models.Model):
 
8
    name = models.CharField(max_length=20)
 
9
 
 
10
    class Meta:
 
11
        ordering = ('name',)
 
12
 
 
13
    def __unicode__(self):
 
14
        return self.name
 
15
 
 
16
class Car(models.Model):
 
17
    name = models.CharField(max_length=20)
 
18
    default_parts = models.ManyToManyField(Part)
 
19
    optional_parts = models.ManyToManyField(Part, related_name='cars_optional')
 
20
 
 
21
    class Meta:
 
22
        ordering = ('name',)
 
23
 
 
24
    def __unicode__(self):
 
25
        return self.name
 
26
 
 
27
class SportsCar(Car):
 
28
    price = models.IntegerField()
 
29
 
 
30
class Person(models.Model):
 
31
    name = models.CharField(max_length=20)
 
32
    fans = models.ManyToManyField('self', related_name='idols', symmetrical=False)
 
33
    friends = models.ManyToManyField('self')
 
34
 
 
35
    class Meta:
 
36
        ordering = ('name',)
 
37
 
 
38
    def __unicode__(self):
 
39
        return self.name
 
40
 
 
41
def m2m_changed_test(signal, sender, **kwargs):
 
42
    print 'm2m_changed signal'
 
43
    print 'instance:', kwargs['instance']
 
44
    print 'action:', kwargs['action']
 
45
    print 'reverse:', kwargs['reverse']
 
46
    print 'model:', kwargs['model']
 
47
    if kwargs['pk_set']:
 
48
        print 'objects:',kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
 
49
 
 
50
 
 
51
__test__ = {'API_TESTS':"""
 
52
# Install a listener on one of the two m2m relations.
 
53
>>> models.signals.m2m_changed.connect(m2m_changed_test, Car.optional_parts.through)
 
54
 
 
55
# Test the add, remove and clear methods on both sides of the
 
56
# many-to-many relation
 
57
 
 
58
>>> c1 = Car.objects.create(name='VW')
 
59
>>> c2 = Car.objects.create(name='BMW')
 
60
>>> c3 = Car.objects.create(name='Toyota')
 
61
>>> p1 = Part.objects.create(name='Wheelset')
 
62
>>> p2 = Part.objects.create(name='Doors')
 
63
>>> p3 = Part.objects.create(name='Engine')
 
64
>>> p4 = Part.objects.create(name='Airbag')
 
65
>>> p5 = Part.objects.create(name='Sunroof')
 
66
 
 
67
# adding a default part to our car - no signal listener installed
 
68
>>> c1.default_parts.add(p5)
 
69
 
 
70
# Now install a listener
 
71
>>> models.signals.m2m_changed.connect(m2m_changed_test, Car.default_parts.through)
 
72
 
 
73
>>> c1.default_parts.add(p1, p2, p3)
 
74
m2m_changed signal
 
75
instance: VW
 
76
action: pre_add
 
77
reverse: False
 
78
model: <class 'modeltests.m2m_signals.models.Part'>
 
79
objects: [<Part: Doors>, <Part: Engine>, <Part: Wheelset>]
 
80
m2m_changed signal
 
81
instance: VW
 
82
action: post_add
 
83
reverse: False
 
84
model: <class 'modeltests.m2m_signals.models.Part'>
 
85
objects: [<Part: Doors>, <Part: Engine>, <Part: Wheelset>]
 
86
 
 
87
# give the BMW and Toyata some doors as well
 
88
>>> p2.car_set.add(c2, c3)
 
89
m2m_changed signal
 
90
instance: Doors
 
91
action: pre_add
 
92
reverse: True
 
93
model: <class 'modeltests.m2m_signals.models.Car'>
 
94
objects: [<Car: BMW>, <Car: Toyota>]
 
95
m2m_changed signal
 
96
instance: Doors
 
97
action: post_add
 
98
reverse: True
 
99
model: <class 'modeltests.m2m_signals.models.Car'>
 
100
objects: [<Car: BMW>, <Car: Toyota>]
 
101
 
 
102
# remove the engine from the VW and the airbag (which is not set but is returned)
 
103
>>> c1.default_parts.remove(p3, p4)
 
104
m2m_changed signal
 
105
instance: VW
 
106
action: pre_remove
 
107
reverse: False
 
108
model: <class 'modeltests.m2m_signals.models.Part'>
 
109
objects: [<Part: Airbag>, <Part: Engine>]
 
110
m2m_changed signal
 
111
instance: VW
 
112
action: post_remove
 
113
reverse: False
 
114
model: <class 'modeltests.m2m_signals.models.Part'>
 
115
objects: [<Part: Airbag>, <Part: Engine>]
 
116
 
 
117
# give the VW some optional parts (second relation to same model)
 
118
>>> c1.optional_parts.add(p4,p5)
 
119
m2m_changed signal
 
120
instance: VW
 
121
action: pre_add
 
122
reverse: False
 
123
model: <class 'modeltests.m2m_signals.models.Part'>
 
124
objects: [<Part: Airbag>, <Part: Sunroof>]
 
125
m2m_changed signal
 
126
instance: VW
 
127
action: post_add
 
128
reverse: False
 
129
model: <class 'modeltests.m2m_signals.models.Part'>
 
130
objects: [<Part: Airbag>, <Part: Sunroof>]
 
131
 
 
132
# add airbag to all the cars (even though the VW already has one)
 
133
>>> p4.cars_optional.add(c1, c2, c3)
 
134
m2m_changed signal
 
135
instance: Airbag
 
136
action: pre_add
 
137
reverse: True
 
138
model: <class 'modeltests.m2m_signals.models.Car'>
 
139
objects: [<Car: BMW>, <Car: Toyota>]
 
140
m2m_changed signal
 
141
instance: Airbag
 
142
action: post_add
 
143
reverse: True
 
144
model: <class 'modeltests.m2m_signals.models.Car'>
 
145
objects: [<Car: BMW>, <Car: Toyota>]
 
146
 
 
147
# remove airbag from the VW (reverse relation with custom related_name)
 
148
>>> p4.cars_optional.remove(c1)
 
149
m2m_changed signal
 
150
instance: Airbag
 
151
action: pre_remove
 
152
reverse: True
 
153
model: <class 'modeltests.m2m_signals.models.Car'>
 
154
objects: [<Car: VW>]
 
155
m2m_changed signal
 
156
instance: Airbag
 
157
action: post_remove
 
158
reverse: True
 
159
model: <class 'modeltests.m2m_signals.models.Car'>
 
160
objects: [<Car: VW>]
 
161
 
 
162
# clear all parts of the VW
 
163
>>> c1.default_parts.clear()
 
164
m2m_changed signal
 
165
instance: VW
 
166
action: pre_clear
 
167
reverse: False
 
168
model: <class 'modeltests.m2m_signals.models.Part'>
 
169
m2m_changed signal
 
170
instance: VW
 
171
action: post_clear
 
172
reverse: False
 
173
model: <class 'modeltests.m2m_signals.models.Part'>
 
174
 
 
175
# take all the doors off of cars
 
176
>>> p2.car_set.clear()
 
177
m2m_changed signal
 
178
instance: Doors
 
179
action: pre_clear
 
180
reverse: True
 
181
model: <class 'modeltests.m2m_signals.models.Car'>
 
182
m2m_changed signal
 
183
instance: Doors
 
184
action: post_clear
 
185
reverse: True
 
186
model: <class 'modeltests.m2m_signals.models.Car'>
 
187
 
 
188
# take all the airbags off of cars (clear reverse relation with custom related_name)
 
189
>>> p4.cars_optional.clear()
 
190
m2m_changed signal
 
191
instance: Airbag
 
192
action: pre_clear
 
193
reverse: True
 
194
model: <class 'modeltests.m2m_signals.models.Car'>
 
195
m2m_changed signal
 
196
instance: Airbag
 
197
action: post_clear
 
198
reverse: True
 
199
model: <class 'modeltests.m2m_signals.models.Car'>
 
200
 
 
201
# alternative ways of setting relation:
 
202
 
 
203
>>> c1.default_parts.create(name='Windows')
 
204
m2m_changed signal
 
205
instance: VW
 
206
action: pre_add
 
207
reverse: False
 
208
model: <class 'modeltests.m2m_signals.models.Part'>
 
209
objects: [<Part: Windows>]
 
210
m2m_changed signal
 
211
instance: VW
 
212
action: post_add
 
213
reverse: False
 
214
model: <class 'modeltests.m2m_signals.models.Part'>
 
215
objects: [<Part: Windows>]
 
216
<Part: Windows>
 
217
 
 
218
# direct assignment clears the set first, then adds
 
219
>>> c1.default_parts = [p1,p2,p3]
 
220
m2m_changed signal
 
221
instance: VW
 
222
action: pre_clear
 
223
reverse: False
 
224
model: <class 'modeltests.m2m_signals.models.Part'>
 
225
m2m_changed signal
 
226
instance: VW
 
227
action: post_clear
 
228
reverse: False
 
229
model: <class 'modeltests.m2m_signals.models.Part'>
 
230
m2m_changed signal
 
231
instance: VW
 
232
action: pre_add
 
233
reverse: False
 
234
model: <class 'modeltests.m2m_signals.models.Part'>
 
235
objects: [<Part: Doors>, <Part: Engine>, <Part: Wheelset>]
 
236
m2m_changed signal
 
237
instance: VW
 
238
action: post_add
 
239
reverse: False
 
240
model: <class 'modeltests.m2m_signals.models.Part'>
 
241
objects: [<Part: Doors>, <Part: Engine>, <Part: Wheelset>]
 
242
 
 
243
# Check that signals still work when model inheritance is involved
 
244
>>> c4 = SportsCar.objects.create(name='Bugatti', price='1000000')
 
245
>>> c4.default_parts = [p2]
 
246
m2m_changed signal
 
247
instance: Bugatti
 
248
action: pre_clear
 
249
reverse: False
 
250
model: <class 'modeltests.m2m_signals.models.Part'>
 
251
m2m_changed signal
 
252
instance: Bugatti
 
253
action: post_clear
 
254
reverse: False
 
255
model: <class 'modeltests.m2m_signals.models.Part'>
 
256
m2m_changed signal
 
257
instance: Bugatti
 
258
action: pre_add
 
259
reverse: False
 
260
model: <class 'modeltests.m2m_signals.models.Part'>
 
261
objects: [<Part: Doors>]
 
262
m2m_changed signal
 
263
instance: Bugatti
 
264
action: post_add
 
265
reverse: False
 
266
model: <class 'modeltests.m2m_signals.models.Part'>
 
267
objects: [<Part: Doors>]
 
268
 
 
269
>>> p3.car_set.add(c4)
 
270
m2m_changed signal
 
271
instance: Engine
 
272
action: pre_add
 
273
reverse: True
 
274
model: <class 'modeltests.m2m_signals.models.Car'>
 
275
objects: [<Car: Bugatti>]
 
276
m2m_changed signal
 
277
instance: Engine
 
278
action: post_add
 
279
reverse: True
 
280
model: <class 'modeltests.m2m_signals.models.Car'>
 
281
objects: [<Car: Bugatti>]
 
282
 
 
283
# Now test m2m relations with self
 
284
>>> p1 = Person.objects.create(name='Alice')
 
285
>>> p2 = Person.objects.create(name='Bob')
 
286
>>> p3 = Person.objects.create(name='Chuck')
 
287
>>> p4 = Person.objects.create(name='Daisy')
 
288
 
 
289
>>> models.signals.m2m_changed.connect(m2m_changed_test, Person.fans.through)
 
290
>>> models.signals.m2m_changed.connect(m2m_changed_test, Person.friends.through)
 
291
 
 
292
>>> p1.friends = [p2, p3]
 
293
m2m_changed signal
 
294
instance: Alice
 
295
action: pre_clear
 
296
reverse: False
 
297
model: <class 'modeltests.m2m_signals.models.Person'>
 
298
m2m_changed signal
 
299
instance: Alice
 
300
action: post_clear
 
301
reverse: False
 
302
model: <class 'modeltests.m2m_signals.models.Person'>
 
303
m2m_changed signal
 
304
instance: Alice
 
305
action: pre_add
 
306
reverse: False
 
307
model: <class 'modeltests.m2m_signals.models.Person'>
 
308
objects: [<Person: Bob>, <Person: Chuck>]
 
309
m2m_changed signal
 
310
instance: Alice
 
311
action: post_add
 
312
reverse: False
 
313
model: <class 'modeltests.m2m_signals.models.Person'>
 
314
objects: [<Person: Bob>, <Person: Chuck>]
 
315
 
 
316
>>> p1.fans = [p4]
 
317
m2m_changed signal
 
318
instance: Alice
 
319
action: pre_clear
 
320
reverse: False
 
321
model: <class 'modeltests.m2m_signals.models.Person'>
 
322
m2m_changed signal
 
323
instance: Alice
 
324
action: post_clear
 
325
reverse: False
 
326
model: <class 'modeltests.m2m_signals.models.Person'>
 
327
m2m_changed signal
 
328
instance: Alice
 
329
action: pre_add
 
330
reverse: False
 
331
model: <class 'modeltests.m2m_signals.models.Person'>
 
332
objects: [<Person: Daisy>]
 
333
m2m_changed signal
 
334
instance: Alice
 
335
action: post_add
 
336
reverse: False
 
337
model: <class 'modeltests.m2m_signals.models.Person'>
 
338
objects: [<Person: Daisy>]
 
339
 
 
340
>>> p3.idols = [p1,p2]
 
341
m2m_changed signal
 
342
instance: Chuck
 
343
action: pre_clear
 
344
reverse: True
 
345
model: <class 'modeltests.m2m_signals.models.Person'>
 
346
m2m_changed signal
 
347
instance: Chuck
 
348
action: post_clear
 
349
reverse: True
 
350
model: <class 'modeltests.m2m_signals.models.Person'>
 
351
m2m_changed signal
 
352
instance: Chuck
 
353
action: pre_add
 
354
reverse: True
 
355
model: <class 'modeltests.m2m_signals.models.Person'>
 
356
objects: [<Person: Alice>, <Person: Bob>]
 
357
m2m_changed signal
 
358
instance: Chuck
 
359
action: post_add
 
360
reverse: True
 
361
model: <class 'modeltests.m2m_signals.models.Person'>
 
362
objects: [<Person: Alice>, <Person: Bob>]
 
363
 
 
364
# Cleanup - disconnect all signal handlers
 
365
>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Car.default_parts.through)
 
366
>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Car.optional_parts.through)
 
367
>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Person.fans.through)
 
368
>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Person.friends.through)
 
369
 
 
370
"""}