Ticket #13552: 13552-take-1.diff
File 13552-take-1.diff, 14.0 KB (added by , 14 years ago) |
---|
-
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py
a b 456 456 meta = cls._meta 457 457 458 458 if origin and not meta.auto_created: 459 signals.pre_save.send(sender=origin, instance=self, raw=raw )459 signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using) 460 460 461 461 # If we are in a raw save, save the object exactly as presented. 462 462 # That means that we don't try to be smart about saving attributes … … 540 540 # Signal that the save is complete 541 541 if origin and not meta.auto_created: 542 542 signals.post_save.send(sender=origin, instance=self, 543 created=(not record_exists), raw=raw )543 created=(not record_exists), raw=raw, using=using) 544 544 545 545 save_base.alters_data = True 546 546 -
django/db/models/fields/related.py
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
a b 566 566 # duplicate data row for symmetrical reverse entries. 567 567 signals.m2m_changed.send(sender=rel.through, action='pre_add', 568 568 instance=self.instance, reverse=self.reverse, 569 model=self.model, pk_set=new_ids )569 model=self.model, pk_set=new_ids, using=db) 570 570 # Add the ones that aren't there already 571 571 for obj_id in new_ids: 572 572 self.through._default_manager.using(db).create(**{ … … 578 578 # duplicate data row for symmetrical reverse entries. 579 579 signals.m2m_changed.send(sender=rel.through, action='post_add', 580 580 instance=self.instance, reverse=self.reverse, 581 model=self.model, pk_set=new_ids )581 model=self.model, pk_set=new_ids, using=db) 582 582 583 583 def _remove_items(self, source_field_name, target_field_name, *objs): 584 584 # source_col_name: the PK colname in join_table for the source object … … 594 594 old_ids.add(obj.pk) 595 595 else: 596 596 old_ids.add(obj) 597 # Work out what DB we're operating on 598 db = router.db_for_write(self.through.__class__, instance=self.instance) 599 # Send a signal to the other end if need be. 597 600 if self.reverse or source_field_name == self.source_field_name: 598 601 # Don't send the signal when we are deleting the 599 602 # duplicate data row for symmetrical reverse entries. 600 603 signals.m2m_changed.send(sender=rel.through, action="pre_remove", 601 604 instance=self.instance, reverse=self.reverse, 602 model=self.model, pk_set=old_ids )605 model=self.model, pk_set=old_ids, using=db) 603 606 # Remove the specified objects from the join table 604 db = router.db_for_write(self.through.__class__, instance=self.instance)605 607 self.through._default_manager.using(db).filter(**{ 606 608 source_field_name: self._pk_val, 607 609 '%s__in' % target_field_name: old_ids … … 611 613 # duplicate data row for symmetrical reverse entries. 612 614 signals.m2m_changed.send(sender=rel.through, action="post_remove", 613 615 instance=self.instance, reverse=self.reverse, 614 model=self.model, pk_set=old_ids )616 model=self.model, pk_set=old_ids, using=db) 615 617 616 618 def _clear_items(self, source_field_name): 619 db = router.db_for_write(self.through.__class__, instance=self.instance) 617 620 # source_col_name: the PK colname in join_table for the source object 618 621 if self.reverse or source_field_name == self.source_field_name: 619 622 # Don't send the signal when we are clearing the 620 623 # duplicate data rows for symmetrical reverse entries. 621 624 signals.m2m_changed.send(sender=rel.through, action="pre_clear", 622 625 instance=self.instance, reverse=self.reverse, 623 model=self.model, pk_set=None) 624 db = router.db_for_write(self.through.__class__, instance=self.instance) 626 model=self.model, pk_set=None, using=db) 625 627 self.through._default_manager.using(db).filter(**{ 626 628 source_field_name: self._pk_val 627 629 }).delete() … … 630 632 # duplicate data rows for symmetrical reverse entries. 631 633 signals.m2m_changed.send(sender=rel.through, action="post_clear", 632 634 instance=self.instance, reverse=self.reverse, 633 model=self.model, pk_set=None )635 model=self.model, pk_set=None, using=db) 634 636 635 637 return ManyRelatedManager 636 638 -
django/db/models/query.py
diff --git a/django/db/models/query.py b/django/db/models/query.py
a b 1311 1311 # Pre-notify all instances to be deleted. 1312 1312 for pk_val, instance in items: 1313 1313 if not cls._meta.auto_created: 1314 signals.pre_delete.send(sender=cls, instance=instance) 1314 signals.pre_delete.send(sender=cls, instance=instance, \ 1315 using=using) 1315 1316 1316 1317 pk_list = [pk for pk,instance in items] 1317 1318 … … 1343 1344 setattr(instance, field.attname, None) 1344 1345 1345 1346 if not cls._meta.auto_created: 1346 signals.post_delete.send(sender=cls, instance=instance )1347 signals.post_delete.send(sender=cls, instance=instance, using=using) 1347 1348 setattr(instance, cls._meta.pk.attname, None) 1348 1349 1349 1350 if forced_managed: -
django/db/models/signals.py
diff --git a/django/db/models/signals.py b/django/db/models/signals.py
a b 5 5 pre_init = Signal(providing_args=["instance", "args", "kwargs"]) 6 6 post_init = Signal(providing_args=["instance"]) 7 7 8 pre_save = Signal(providing_args=["instance", "raw" ])9 post_save = Signal(providing_args=["instance", "raw", "created" ])8 pre_save = Signal(providing_args=["instance", "raw", "using"]) 9 post_save = Signal(providing_args=["instance", "raw", "created", "using"]) 10 10 11 pre_delete = Signal(providing_args=["instance" ])12 post_delete = Signal(providing_args=["instance" ])11 pre_delete = Signal(providing_args=["instance", "using"]) 12 post_delete = Signal(providing_args=["instance", "using"]) 13 13 14 14 post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) 15 15 16 m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set" ])16 m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"]) -
docs/ref/signals.txt
diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt
a b 113 113 ``instance`` 114 114 The actual instance being saved. 115 115 116 ``using`` 117 The database alias being used. 118 116 119 post_save 117 120 --------- 118 121 … … 133 136 ``created`` 134 137 A boolean; ``True`` if a new record was created. 135 138 139 ``using`` 140 The database alias being used. 141 136 142 pre_delete 137 143 ---------- 138 144 … … 150 156 ``instance`` 151 157 The actual instance being deleted. 152 158 159 ``using`` 160 The database alias being used. 161 153 162 post_delete 154 163 ----------- 155 164 … … 170 179 Note that the object will no longer be in the database, so be very 171 180 careful what you do with this instance. 172 181 182 ``using`` 183 The database alias being used. 184 173 185 m2m_changed 174 186 ----------- 175 187 … … 228 240 229 241 For the ``"clear"`` action, this is ``None``. 230 242 243 ``using`` 244 The database alias being used. 245 231 246 For example, if a ``Pizza`` can have multiple ``Topping`` objects, modeled 232 247 like this: 233 248 … … 266 281 ``Pizza``) 267 282 268 283 ``pk_set`` ``[t.id]`` (since only ``Topping t`` was added to the relation) 284 285 ``using`` ``"default"`` (since the default router sends writes here) 269 286 ============== ============================================================ 270 287 271 288 And if we would then do something like this: … … 293 310 294 311 ``pk_set`` ``[p.id]`` (since only ``Pizza p`` was removed from the 295 312 relation) 313 314 ``using`` ``"default"`` (since the default router sends writes here) 296 315 ============== ============================================================ 297 316 298 317 class_prepared -
tests/modeltests/signals/models.py
diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py
a b 11 11 def __unicode__(self): 12 12 return u"%s %s" % (self.first_name, self.last_name) 13 13 14 class Project(models.Model): 15 people = models.ManyToManyField(Person) 16 17 14 18 def pre_save_test(signal, sender, instance, **kwargs): 15 19 print 'pre_save signal,', instance 16 20 if kwargs.get('raw'): -
tests/modeltests/signals/tests.py
diff --git a/tests/modeltests/signals/tests.py b/tests/modeltests/signals/tests.py
a b 1 1 from django.db.models import signals 2 from django.db import DEFAULT_DB_ALIAS, router 2 3 from django.test import TestCase 3 from modeltests.signals.models import Person 4 from modeltests.signals.models import Person, Project 4 5 5 class MyReceiver(object): 6 class DisconnectReceiver(object): 7 """ 8 Used to test the disconnection of signals. 9 """ 6 10 def __init__(self, param): 7 11 self.param = param 8 12 self._run = False … … 10 14 def __call__(self, signal, sender, **kwargs): 11 15 self._run = True 12 16 signal.disconnect(receiver=self, sender=sender) 17 18 class DatabaseReceiver(object): 19 """ 20 Used in the tests for the database argument in signals (#13552) 21 """ 22 def __init__(self): 23 self._database = None 24 25 def __call__(self, signal, sender, **kwargs): 26 self._database = kwargs['using'] 27 28 class TestRouter(object): 29 """ 30 A router that sends all writes to the other database. 31 """ 32 def db_for_write(self, model, **hints): 33 return "other" 13 34 14 35 class SignalTests(TestCase): 36 37 multi_db = True 38 39 def _write_to_other(self): 40 "Sends all writes to 'other'." 41 self.old_routers = router.routers 42 router.routers = [TestRouter()] 43 44 def _write_to_default(self): 45 "Sends all writes to the default DB" 46 router.routers = self.old_routers 47 15 48 def test_disconnect_in_dispatch(self): 16 49 """ 17 50 Test that signals that disconnect when being called don't mess future 18 51 dispatching. 19 52 """ 20 a, b = MyReceiver(1), MyReceiver(2)53 a, b = DisconnectReceiver(1), DisconnectReceiver(2) 21 54 signals.post_save.connect(sender=Person, receiver=a) 22 55 signals.post_save.connect(sender=Person, receiver=b) 23 56 p = Person.objects.create(first_name='John', last_name='Smith') … … 25 58 self.failUnless(a._run) 26 59 self.failUnless(b._run) 27 60 self.assertEqual(signals.post_save.receivers, []) 28 61 62 def test_database_arg_save_and_delete(self): 63 """ 64 Tests that the pre/post_save signal contains the correct database. 65 (#13552) 66 """ 67 # Make some signal receivers 68 pre_save_receiver = DatabaseReceiver() 69 post_save_receiver = DatabaseReceiver() 70 pre_delete_receiver = DatabaseReceiver() 71 post_delete_receiver = DatabaseReceiver() 72 # Make model and connect receivers 73 signals.pre_save.connect(sender=Person, receiver=pre_save_receiver) 74 signals.post_save.connect(sender=Person, receiver=post_save_receiver) 75 signals.pre_delete.connect(sender=Person, receiver=pre_delete_receiver) 76 signals.post_delete.connect(sender=Person, receiver=post_delete_receiver) 77 p = Person.objects.create(first_name='Darth', last_name='Vader') 78 # Save and test receivers got calls 79 p.save() 80 self.assertEqual(pre_save_receiver._database, DEFAULT_DB_ALIAS) 81 self.assertEqual(post_save_receiver._database, DEFAULT_DB_ALIAS) 82 # Delete, and test 83 p.delete() 84 self.assertEqual(pre_delete_receiver._database, DEFAULT_DB_ALIAS) 85 self.assertEqual(post_delete_receiver._database, DEFAULT_DB_ALIAS) 86 # Save again to a different database 87 p.save(using="other") 88 self.assertEqual(pre_save_receiver._database, "other") 89 self.assertEqual(post_save_receiver._database, "other") 90 # Delete, and test 91 p.delete(using="other") 92 self.assertEqual(pre_delete_receiver._database, "other") 93 self.assertEqual(post_delete_receiver._database, "other") 94 95 def test_database_arg_m2m(self): 96 """ 97 Test that the m2m_changed signal has a correct database arg (#13552) 98 """ 99 # Make a receiver 100 receiver = DatabaseReceiver() 101 # Connect it, and make the models 102 signals.m2m_changed.connect(receiver=receiver) 103 pe = Person.objects.create(first_name='Jane', last_name='Smith') 104 pr = Project.objects.create() 105 # Test addition 106 pr.people.add(pe) 107 self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) 108 self._write_to_other() 109 pr.people.add(pe) 110 self._write_to_default() 111 self.assertEqual(receiver._database, "other") 112 # Test removal 113 pr.people.remove(pe) 114 self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) 115 self._write_to_other() 116 pr.people.remove(pe) 117 self._write_to_default() 118 self.assertEqual(receiver._database, "other") 119 # Test clearing 120 pr.people.clear() 121 self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) 122 self._write_to_other() 123 pr.people.clear() 124 self._write_to_default() 125 self.assertEqual(receiver._database, "other") 126 No newline at end of file