Ticket #13552: 13552-take-3.diff

File 13552-take-3.diff, 14.3 KB (added by Andrew Godwin, 14 years ago)

Also include an AUTHORS commit.

  • AUTHORS

    diff --git a/AUTHORS b/AUTHORS
    a b  
    189189    martin.glueck@gmail.com
    190190    Artyom Gnilov <boobsd@gmail.com>
    191191    Ben Godfrey <http://aftnn.org>
     192    Andrew Godwin <andrew@aeracode.org>
    192193    GomoX <gomo@datafull.com>
    193194    Guilherme Mesquita Gondim <semente@taurinus.org>
    194195    Mario Gonzalez <gonzalemario@gmail.com>
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    a b  
    456456            meta = cls._meta
    457457
    458458        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)
    460460
    461461        # If we are in a raw save, save the object exactly as presented.
    462462        # That means that we don't try to be smart about saving attributes
     
    540540        # Signal that the save is complete
    541541        if origin and not meta.auto_created:
    542542            signals.post_save.send(sender=origin, instance=self,
    543                 created=(not record_exists), raw=raw)
     543                created=(not record_exists), raw=raw, using=using)
    544544
    545545    save_base.alters_data = True
    546546
  • django/db/models/fields/related.py

    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    a b  
    566566                    # duplicate data row for symmetrical reverse entries.
    567567                    signals.m2m_changed.send(sender=rel.through, action='pre_add',
    568568                        instance=self.instance, reverse=self.reverse,
    569                         model=self.model, pk_set=new_ids)
     569                        model=self.model, pk_set=new_ids, using=db)
    570570                # Add the ones that aren't there already
    571571                for obj_id in new_ids:
    572572                    self.through._default_manager.using(db).create(**{
     
    578578                    # duplicate data row for symmetrical reverse entries.
    579579                    signals.m2m_changed.send(sender=rel.through, action='post_add',
    580580                        instance=self.instance, reverse=self.reverse,
    581                         model=self.model, pk_set=new_ids)
     581                        model=self.model, pk_set=new_ids, using=db)
    582582
    583583        def _remove_items(self, source_field_name, target_field_name, *objs):
    584584            # source_col_name: the PK colname in join_table for the source object
     
    594594                        old_ids.add(obj.pk)
    595595                    else:
    596596                        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.
    597600                if self.reverse or source_field_name == self.source_field_name:
    598601                    # Don't send the signal when we are deleting the
    599602                    # duplicate data row for symmetrical reverse entries.
    600603                    signals.m2m_changed.send(sender=rel.through, action="pre_remove",
    601604                        instance=self.instance, reverse=self.reverse,
    602                         model=self.model, pk_set=old_ids)
     605                        model=self.model, pk_set=old_ids, using=db)
    603606                # Remove the specified objects from the join table
    604                 db = router.db_for_write(self.through.__class__, instance=self.instance)
    605607                self.through._default_manager.using(db).filter(**{
    606608                    source_field_name: self._pk_val,
    607609                    '%s__in' % target_field_name: old_ids
     
    611613                    # duplicate data row for symmetrical reverse entries.
    612614                    signals.m2m_changed.send(sender=rel.through, action="post_remove",
    613615                        instance=self.instance, reverse=self.reverse,
    614                         model=self.model, pk_set=old_ids)
     616                        model=self.model, pk_set=old_ids, using=db)
    615617
    616618        def _clear_items(self, source_field_name):
     619            db = router.db_for_write(self.through.__class__, instance=self.instance)
    617620            # source_col_name: the PK colname in join_table for the source object
    618621            if self.reverse or source_field_name == self.source_field_name:
    619622                # Don't send the signal when we are clearing the
    620623                # duplicate data rows for symmetrical reverse entries.
    621624                signals.m2m_changed.send(sender=rel.through, action="pre_clear",
    622625                    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)
    625627            self.through._default_manager.using(db).filter(**{
    626628                source_field_name: self._pk_val
    627629            }).delete()
     
    630632                # duplicate data rows for symmetrical reverse entries.
    631633                signals.m2m_changed.send(sender=rel.through, action="post_clear",
    632634                    instance=self.instance, reverse=self.reverse,
    633                     model=self.model, pk_set=None)
     635                    model=self.model, pk_set=None, using=db)
    634636
    635637    return ManyRelatedManager
    636638
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    a b  
    13111311            # Pre-notify all instances to be deleted.
    13121312            for pk_val, instance in items:
    13131313                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)
    13151316
    13161317            pk_list = [pk for pk,instance in items]
    13171318
     
    13431344                        setattr(instance, field.attname, None)
    13441345
    13451346                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)
    13471348                setattr(instance, cls._meta.pk.attname, None)
    13481349
    13491350        if forced_managed:
  • django/db/models/signals.py

    diff --git a/django/db/models/signals.py b/django/db/models/signals.py
    a b  
    55pre_init = Signal(providing_args=["instance", "args", "kwargs"])
    66post_init = Signal(providing_args=["instance"])
    77
    8 pre_save = Signal(providing_args=["instance", "raw"])
    9 post_save = Signal(providing_args=["instance", "raw", "created"])
     8pre_save = Signal(providing_args=["instance", "raw", "using"])
     9post_save = Signal(providing_args=["instance", "raw", "created", "using"])
    1010
    11 pre_delete = Signal(providing_args=["instance"])
    12 post_delete = Signal(providing_args=["instance"])
     11pre_delete = Signal(providing_args=["instance", "using"])
     12post_delete = Signal(providing_args=["instance", "using"])
    1313
    1414post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"])
    1515
    16 m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set"])
     16m2m_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  
    113113    ``instance``
    114114        The actual instance being saved.
    115115
     116    ``using``
     117        The database alias being used.
     118
    116119post_save
    117120---------
    118121
     
    133136    ``created``
    134137        A boolean; ``True`` if a new record was created.
    135138
     139    ``using``
     140        The database alias being used.
     141
    136142pre_delete
    137143----------
    138144
     
    150156    ``instance``
    151157        The actual instance being deleted.
    152158
     159    ``using``
     160        The database alias being used.
     161
    153162post_delete
    154163-----------
    155164
     
    170179        Note that the object will no longer be in the database, so be very
    171180        careful what you do with this instance.
    172181
     182    ``using``
     183        The database alias being used.
     184
    173185m2m_changed
    174186-----------
    175187
     
    228240
    229241        For the ``"clear"`` action, this is ``None``.
    230242
     243    ``using``
     244        The database alias being used.
     245
    231246For example, if a ``Pizza`` can have multiple ``Topping`` objects, modeled
    232247like this:
    233248
     
    266281                    ``Pizza``)
    267282
    268283    ``pk_set``      ``[t.id]`` (since only ``Topping t`` was added to the relation)
     284       
     285        ``using``       ``"default"`` (since the default router sends writes here)
    269286    ==============  ============================================================
    270287
    271288And if we would then do something like this:
     
    293310
    294311    ``pk_set``      ``[p.id]`` (since only ``Pizza p`` was removed from the
    295312                    relation)
     313       
     314        ``using``       ``"default"`` (since the default router sends writes here)
    296315    ==============  ============================================================
    297316
    298317class_prepared
  • tests/modeltests/signals/models.py

    diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py
    a b  
    1111    def __unicode__(self):
    1212        return u"%s %s" % (self.first_name, self.last_name)
    1313
     14class Project(models.Model):
     15    people = models.ManyToManyField(Person)
     16
     17
    1418def pre_save_test(signal, sender, instance, **kwargs):
    1519    print 'pre_save signal,', instance
    1620    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  
    11from django.db.models import signals
     2from django.db import DEFAULT_DB_ALIAS, router
    23from django.test import TestCase
    3 from modeltests.signals.models import Person
     4from modeltests.signals.models import Person, Project
    45
    5 class MyReceiver(object):
     6class DisconnectReceiver(object):
     7    """
     8    Used to test the disconnection of signals.
     9    """
    610    def __init__(self, param):
    711        self.param = param
    812        self._run = False
     
    1014    def __call__(self, signal, sender, **kwargs):
    1115        self._run = True
    1216        signal.disconnect(receiver=self, sender=sender)
     17 
     18class DatabaseReceiver(object):
     19    """
     20    Used in the tests for the database argument in signals (#13552)
     21    """
     22    def __call__(self, signal, sender, **kwargs):
     23        self._database = kwargs['using']
     24   
     25class TestRouter(object):
     26    """
     27    A router that sends all writes to the other database.
     28    """
     29    def db_for_write(self, model, **hints):
     30        return "other"
    1331
    1432class SignalTests(TestCase):
     33   
     34    multi_db = True
     35   
     36    def _write_to_other(self):
     37        "Sends all writes to 'other'."
     38        self.old_routers = router.routers
     39        router.routers = [TestRouter()]
     40
     41    def _write_to_default(self):
     42        "Sends all writes to the default DB"
     43        router.routers = self.old_routers
     44   
    1545    def test_disconnect_in_dispatch(self):
    1646        """
    1747        Test that signals that disconnect when being called don't mess future
    1848        dispatching.
    1949        """
    20         a, b = MyReceiver(1), MyReceiver(2)
     50        a, b = DisconnectReceiver(1), DisconnectReceiver(2)
    2151        signals.post_save.connect(sender=Person, receiver=a)
    2252        signals.post_save.connect(sender=Person, receiver=b)
    2353        p = Person.objects.create(first_name='John', last_name='Smith')
     
    2555        self.failUnless(a._run)
    2656        self.failUnless(b._run)
    2757        self.assertEqual(signals.post_save.receivers, [])
    28        
     58   
     59    def test_database_arg_save_and_delete(self):
     60        """
     61        Tests that the pre/post_save signal contains the correct database.
     62        (#13552)
     63        """
     64        # Make some signal receivers
     65        pre_save_receiver = DatabaseReceiver()
     66        post_save_receiver = DatabaseReceiver()
     67        pre_delete_receiver = DatabaseReceiver()
     68        post_delete_receiver = DatabaseReceiver()
     69        # Make model and connect receivers
     70        signals.pre_save.connect(sender=Person, receiver=pre_save_receiver)
     71        signals.post_save.connect(sender=Person, receiver=post_save_receiver)
     72        signals.pre_delete.connect(sender=Person, receiver=pre_delete_receiver)
     73        signals.post_delete.connect(sender=Person, receiver=post_delete_receiver)
     74        p = Person.objects.create(first_name='Darth', last_name='Vader')
     75        # Save and test receivers got calls
     76        p.save()
     77        self.assertEqual(pre_save_receiver._database, DEFAULT_DB_ALIAS)
     78        self.assertEqual(post_save_receiver._database, DEFAULT_DB_ALIAS)
     79        # Delete, and test
     80        p.delete()
     81        self.assertEqual(pre_delete_receiver._database, DEFAULT_DB_ALIAS)
     82        self.assertEqual(post_delete_receiver._database, DEFAULT_DB_ALIAS)
     83        # Save again to a different database
     84        p.save(using="other")
     85        self.assertEqual(pre_save_receiver._database, "other")
     86        self.assertEqual(post_save_receiver._database, "other")
     87        # Delete, and test
     88        p.delete(using="other")
     89        self.assertEqual(pre_delete_receiver._database, "other")
     90        self.assertEqual(post_delete_receiver._database, "other")
     91   
     92    def test_database_arg_m2m(self):
     93        """
     94        Test that the m2m_changed signal has a correct database arg (#13552)
     95        """
     96        # Make a receiver
     97        receiver = DatabaseReceiver()
     98        # Connect it, and make the models
     99        signals.m2m_changed.connect(receiver=receiver)
     100        pe = Person.objects.create(first_name='Jane', last_name='Smith')
     101        pr = Project.objects.create()
     102        # Test addition
     103        pr.people.add(pe)
     104        self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
     105        self._write_to_other()
     106        pr.people.add(pe)
     107        self._write_to_default()
     108        self.assertEqual(receiver._database, "other")
     109        # Test removal
     110        pr.people.remove(pe)
     111        self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
     112        self._write_to_other()
     113        pr.people.remove(pe)
     114        self._write_to_default()
     115        self.assertEqual(receiver._database, "other")
     116        # Test clearing
     117        pr.people.clear()
     118        self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
     119        self._write_to_other()
     120        pr.people.clear()
     121        self._write_to_default()
     122        self.assertEqual(receiver._database, "other")
     123 No newline at end of file
Back to Top