Ticket #6778: patch.diff

File patch.diff, 7.4 KB (added by mysliceof314, 7 years ago)
  • django/django/db/models/fields/related.py

     
    424424            return obj, created
    425425        get_or_create.alters_data = True
    426426
     427        def _add_items_signal(func):
     428          def _add_items(self, source_col_name, target_col_name, *objs):
     429            func(self, source_col_name, target_col_name, *objs)
     430            if objs:
     431              self._object_added_signal(objs)
     432          return _add_items
     433         
     434        def _remove_items_signal(func):
     435            def _remove_items(self, source_col_name, target_col_name, *objs):
     436              func(self, source_col_name, target_col_name, *objs)
     437              if objs:
     438                self._object_removed_signal(objs)
     439            return _remove_items
     440 
     441        def _clear_items_signal(func):
     442          def _clear_items(self, source_col_name):
     443            # check if there are any registered receivers
     444            # if none, don't waste time performing query
     445            will_signal = self._test_signal_has_receivers(signals.m2m_related_data_removed)
     446            if will_signal:
     447              removed = list(self.all())
     448            func(self,source_col_name)
     449            # check again, why fire off signal if no one is listening?
     450            if will_signal:
     451              self._object_removed_signal(removed)
     452          return _clear_items
     453
    427454        def _add_items(self, source_col_name, target_col_name, *objs):
    428455            # join_table: name of the m2m link table
    429456            # source_col_name: the PK colname in join_table for the source object
     
    455482                        [self._pk_val, obj_id])
    456483                transaction.commit_unless_managed()
    457484
     485        # add fire off signal when items are added
     486        _add_items = _add_items_signal(_add_items)
     487
    458488        def _remove_items(self, source_col_name, target_col_name, *objs):
    459489            # source_col_name: the PK colname in join_table for the source object
    460490            # target_col_name: the PK colname in join_table for the target object
     
    477507                    [self._pk_val] + list(old_ids))
    478508                transaction.commit_unless_managed()
    479509
     510        _remove_items = _remove_items_signal(_remove_items)
     511
    480512        def _clear_items(self, source_col_name):
    481513            # source_col_name: the PK colname in join_table for the source object
    482514            cursor = connection.cursor()
     
    484516                (self.join_table, source_col_name),
    485517                [self._pk_val])
    486518            transaction.commit_unless_managed()
     519           
     520        _clear_items = _clear_items_signal(_clear_items)
    487521
     522        def _object_removed_signal(self, objs):
     523            signals.m2m_related_data_removed.send(sender=self.instance, objs_removed=objs, join_table=self.join_table )
     524       
     525        def _object_added_signal(self,objs):
     526            signals.m2m_related_data_added.send(sender=self.instance, objs_added=objs , join_table = self.join_table)   
     527
     528        def _test_signal_has_receivers(self, signal):
     529          return len(signal.receivers) > 0
     530         
    488531    return ManyRelatedManager
    489532
    490533class ManyRelatedObjectsDescriptor(object):
  • django/django/db/models/signals.py

     
    1212post_delete = Signal(providing_args=["instance"])
    1313
    1414post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"])
     15
     16m2m_related_data_added = Signal(providing_args=['objs_added','join_table'])
     17m2m_related_data_removed = Signal(providing_args=['objs_removed','join_table'])
     18 No newline at end of file
  • django/tests/modeltests/m2m_signals/models.py

     
     1"""
     2Testing signals fired when item(s) are added to a many-to-many relation,
     3when items are removed and when the relation is cleared
     4"""
     5print('testing m2m signals')
     6
     7from django.db import models
     8from django.db.models import signals
     9
     10class Pizza(models.Model):
     11    name = models.CharField(max_length=60)
     12
     13class Topping(models.Model):
     14    name = models.CharField(max_length=50)
     15    pizzas = models.ManyToManyField(Pizza) 
     16
     17def relation_made(signal, sender, **named):
     18    names = str([str(x.name) for x in named['objs_added']])
     19    print '%s just associated with %s' % (sender.name, names)
     20
     21def relation_unmade(signal, sender, **named):
     22    names = str([str(x.name) for x in named['objs_removed']])
     23    print '%s just disassociated with %s' % (sender.name, names)
     24   
     25signals.m2m_related_data_added.connect(relation_made)
     26signals.m2m_related_data_removed.connect(relation_unmade)
     27
     28__test__ = {'API_TESTS':"""
     29>>> p1, p2, p3 = Pizza(name='veggie'), Pizza(name='cheese'), Pizza(name='pepperoni')
     30>>> p1.save()
     31>>> p2.save()
     32>>> p3.save()
     33>>> t1, t2, t3, t4= Topping(name='cheese'), Topping(name='red pepper'), \
     34                    Topping(name='mushrooms'), Topping(name='pepperoni')
     35>>> t1.save()
     36>>> t2.save()
     37>>> t3.save()
     38>>> t4.save()
     39
     40# associate some data
     41>>> p1.topping_set.add(t1,t2,t3)
     42veggie just associated with ['cheese', 'red pepper', 'mushrooms']
     43>>> p2.topping_set.add(t1)
     44cheese just associated with ['cheese']
     45>>> p3.topping_set.add(t1,t3,t4)
     46pepperoni just associated with ['cheese', 'mushrooms', 'pepperoni']
     47>>> p3.topping_set.remove(t3)
     48pepperoni just disassociated with ['mushrooms']
     49>>> p3.topping_set.clear()
     50pepperoni just disassociated with ['cheese', 'pepperoni']
     51>>> t1.pizzas.add(p3)
     52cheese just associated with ['pepperoni']
     53>>> t1.pizzas.clear()
     54cheese just disassociated with ['veggie', 'cheese', 'pepperoni']
     55"""}
  • django/docs/ref/signals.txt

     
    162162
    163163        Note that the object will no longer be in the database, so be very
    164164        careful what you do with this instance.
     165       
     166m2m_related_data_added
     167-----------
    165168
     169.. data:: django.db.models.signals.m2m_related_data_added
     170   :module:
     171   
     172Like :data:`m2m_related_data_removed`, but sent when data is added.
     173
     174Arguments sent with this signal:
     175
     176    ``sender``
     177        The instance which just had data asscoiated with it.
     178
     179    ``objs_added``
     180        The object(s) which were just associated with the sender.
     181       
     182    ``join_table`` 
     183                The join table between the relevant many-to-many relationship.
     184               
     185m2m_related_data_removed
     186-----------
     187
     188.. data:: django.db.models.signals.m2m_related_data_added
     189   :module:
     190   
     191Like :data:`m2m_related_data_added`, but sent when data is removed.
     192
     193Arguments sent with this signal:
     194
     195    ``sender``
     196        The instance which just had data asscoiated with it.
     197
     198    ``objs_removed``
     199        The object(s) which were just disassociated with the sender.
     200       
     201    ``join_table`` 
     202                The join table between the relevant many-to-many relationship.
     203
    166204class_prepared
    167205--------------
    168206
Back to Top