|
@@ -0,0 +1,252 @@
|
|
|
+"""
|
|
|
+Testing signals emitted on changing m2m relations.
|
|
|
+"""
|
|
|
+
|
|
|
+from django.db import models
|
|
|
+
|
|
|
+class Part(models.Model):
|
|
|
+ name = models.CharField(max_length=20)
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ ordering = ('name',)
|
|
|
+
|
|
|
+ def __unicode__(self):
|
|
|
+ return self.name
|
|
|
+
|
|
|
+class Car(models.Model):
|
|
|
+ name = models.CharField(max_length=20)
|
|
|
+ default_parts = models.ManyToManyField(Part)
|
|
|
+ optional_parts = models.ManyToManyField(Part, related_name='cars_optional')
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ ordering = ('name',)
|
|
|
+
|
|
|
+ def __unicode__(self):
|
|
|
+ return self.name
|
|
|
+
|
|
|
+class SportsCar(Car):
|
|
|
+ price = models.IntegerField()
|
|
|
+
|
|
|
+class Person(models.Model):
|
|
|
+ name = models.CharField(max_length=20)
|
|
|
+ fans = models.ManyToManyField('self', related_name='idols', symmetrical=False)
|
|
|
+ friends = models.ManyToManyField('self')
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ ordering = ('name',)
|
|
|
+
|
|
|
+ def __unicode__(self):
|
|
|
+ return self.name
|
|
|
+
|
|
|
+def m2m_changed_test(signal, sender, **kwargs):
|
|
|
+ print 'm2m_changed signal'
|
|
|
+ print 'instance:', kwargs['instance']
|
|
|
+ print 'action:', kwargs['action']
|
|
|
+ print 'reverse:', kwargs['reverse']
|
|
|
+ print 'model:', kwargs['model']
|
|
|
+ if kwargs['pk_set']:
|
|
|
+ print 'objects:',kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
|
|
+
|
|
|
+
|
|
|
+__test__ = {'API_TESTS':"""
|
|
|
+
|
|
|
+>>> models.signals.m2m_changed.connect(m2m_changed_test, Car.optional_parts.through)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+>>> c1 = Car.objects.create(name='VW')
|
|
|
+>>> c2 = Car.objects.create(name='BMW')
|
|
|
+>>> c3 = Car.objects.create(name='Toyota')
|
|
|
+>>> p1 = Part.objects.create(name='Wheelset')
|
|
|
+>>> p2 = Part.objects.create(name='Doors')
|
|
|
+>>> p3 = Part.objects.create(name='Engine')
|
|
|
+>>> p4 = Part.objects.create(name='Airbag')
|
|
|
+>>> p5 = Part.objects.create(name='Sunroof')
|
|
|
+
|
|
|
+
|
|
|
+>>> c1.default_parts.add(p5)
|
|
|
+
|
|
|
+
|
|
|
+>>> models.signals.m2m_changed.connect(m2m_changed_test, Car.default_parts.through)
|
|
|
+
|
|
|
+>>> c1.default_parts.add(p1, p2, p3)
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+objects: [<Part: Doors>, <Part: Engine>, <Part: Wheelset>]
|
|
|
+
|
|
|
+
|
|
|
+>>> p2.car_set.add(c2, c3)
|
|
|
+m2m_changed signal
|
|
|
+instance: Doors
|
|
|
+action: add
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Car'>
|
|
|
+objects: [<Car: BMW>, <Car: Toyota>]
|
|
|
+
|
|
|
+
|
|
|
+>>> c1.default_parts.remove(p3, p4)
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: remove
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+objects: [<Part: Airbag>, <Part: Engine>]
|
|
|
+
|
|
|
+
|
|
|
+>>> c1.optional_parts.add(p4,p5)
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+objects: [<Part: Airbag>, <Part: Sunroof>]
|
|
|
+
|
|
|
+
|
|
|
+>>> p4.cars_optional.add(c1, c2, c3)
|
|
|
+m2m_changed signal
|
|
|
+instance: Airbag
|
|
|
+action: add
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Car'>
|
|
|
+objects: [<Car: BMW>, <Car: Toyota>]
|
|
|
+
|
|
|
+
|
|
|
+>>> p4.cars_optional.remove(c1)
|
|
|
+m2m_changed signal
|
|
|
+instance: Airbag
|
|
|
+action: remove
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Car'>
|
|
|
+objects: [<Car: VW>]
|
|
|
+
|
|
|
+
|
|
|
+>>> c1.default_parts.clear()
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: clear
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+
|
|
|
+
|
|
|
+>>> p2.car_set.clear()
|
|
|
+m2m_changed signal
|
|
|
+instance: Doors
|
|
|
+action: clear
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Car'>
|
|
|
+
|
|
|
+
|
|
|
+>>> p4.cars_optional.clear()
|
|
|
+m2m_changed signal
|
|
|
+instance: Airbag
|
|
|
+action: clear
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Car'>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+>>> c1.default_parts.create(name='Windows')
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+objects: [<Part: Windows>]
|
|
|
+<Part: Windows>
|
|
|
+
|
|
|
+
|
|
|
+>>> c1.default_parts = [p1,p2,p3]
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: clear
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+m2m_changed signal
|
|
|
+instance: VW
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+objects: [<Part: Doors>, <Part: Engine>, <Part: Wheelset>]
|
|
|
+
|
|
|
+
|
|
|
+>>> c4 = SportsCar.objects.create(name='Bugatti', price='1000000')
|
|
|
+>>> c4.default_parts = [p2]
|
|
|
+m2m_changed signal
|
|
|
+instance: Bugatti
|
|
|
+action: clear
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+m2m_changed signal
|
|
|
+instance: Bugatti
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Part'>
|
|
|
+objects: [<Part: Doors>]
|
|
|
+
|
|
|
+>>> p3.car_set.add(c4)
|
|
|
+m2m_changed signal
|
|
|
+instance: Engine
|
|
|
+action: add
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Car'>
|
|
|
+objects: [<Car: Bugatti>]
|
|
|
+
|
|
|
+
|
|
|
+>>> p1 = Person.objects.create(name='Alice')
|
|
|
+>>> p2 = Person.objects.create(name='Bob')
|
|
|
+>>> p3 = Person.objects.create(name='Chuck')
|
|
|
+>>> p4 = Person.objects.create(name='Daisy')
|
|
|
+
|
|
|
+>>> models.signals.m2m_changed.connect(m2m_changed_test, Person.fans.through)
|
|
|
+>>> models.signals.m2m_changed.connect(m2m_changed_test, Person.friends.through)
|
|
|
+
|
|
|
+>>> p1.friends = [p2, p3]
|
|
|
+m2m_changed signal
|
|
|
+instance: Alice
|
|
|
+action: clear
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Person'>
|
|
|
+m2m_changed signal
|
|
|
+instance: Alice
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Person'>
|
|
|
+objects: [<Person: Bob>, <Person: Chuck>]
|
|
|
+
|
|
|
+>>> p1.fans = [p4]
|
|
|
+m2m_changed signal
|
|
|
+instance: Alice
|
|
|
+action: clear
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Person'>
|
|
|
+m2m_changed signal
|
|
|
+instance: Alice
|
|
|
+action: add
|
|
|
+reverse: False
|
|
|
+model: <class 'modeltests.m2m_signals.models.Person'>
|
|
|
+objects: [<Person: Daisy>]
|
|
|
+
|
|
|
+>>> p3.idols = [p1,p2]
|
|
|
+m2m_changed signal
|
|
|
+instance: Chuck
|
|
|
+action: clear
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Person'>
|
|
|
+m2m_changed signal
|
|
|
+instance: Chuck
|
|
|
+action: add
|
|
|
+reverse: True
|
|
|
+model: <class 'modeltests.m2m_signals.models.Person'>
|
|
|
+objects: [<Person: Alice>, <Person: Bob>]
|
|
|
+
|
|
|
+
|
|
|
+>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Car.default_parts.through)
|
|
|
+>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Car.optional_parts.through)
|
|
|
+>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Person.fans.through)
|
|
|
+>>> models.signals.m2m_changed.disconnect(m2m_changed_test, Person.friends.through)
|
|
|
+
|
|
|
+"""}
|