Index: db/models/fields/related.py
===================================================================
--- db/models/fields/related.py	(revision 7049)
+++ db/models/fields/related.py	(working copy)
@@ -266,8 +266,9 @@
     """Creates a manager that subclasses 'superclass' (which is a Manager)
     and adds behavior for many-to-many related objects."""
     class ManyRelatedManager(superclass):
-        def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
-                join_table=None, source_col_name=None, target_col_name=None):
+        def __init__(self, model=None, core_filters=None, instance=None,
+                     symmetrical=None, join_table=None, source_col_name=None,
+                     target_col_name=None, field_name=None):
             super(ManyRelatedManager, self).__init__()
             self.core_filters = core_filters
             self.model = model
@@ -277,6 +278,7 @@
             self.source_col_name = source_col_name
             self.target_col_name = target_col_name
             self._pk_val = self.instance._get_pk_val()
+            self.field_name = field_name
             if self._pk_val is None:
                 raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % model)
 
@@ -339,10 +341,17 @@
                 existing_ids = set([row[0] for row in cursor.fetchall()])
 
                 # Add the ones that aren't there already
-                for obj_id in (new_ids - existing_ids):
+                new_ids = new_ids - existing_ids
+                for obj_id in new_ids:
                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
                         (self.join_table, source_col_name, target_col_name),
                         [self._pk_val, obj_id])
+
+                added_objs = [obj for obj in objs if (isinstance(obj, self.model) and obj._get_pk_val() in new_ids) or obj in new_ids]
+                dispatcher.send(signal=signals.m2m_add_items, sender=self.model,
+                                instance=self.instance, field_name=self.field_name,
+                                objs=added_objs)
+
                 transaction.commit_unless_managed()
 
         def _remove_items(self, source_col_name, target_col_name, *objs):
@@ -365,10 +374,15 @@
                     (self.join_table, source_col_name,
                     target_col_name, ",".join(['%s'] * len(old_ids))),
                     [self._pk_val] + list(old_ids))
+                dispatcher.send(signal=signals.m2m_remove_items, sender=self.model,
+                                instance=self.instance, field_name=self.field_name,
+                                objs=objs)
                 transaction.commit_unless_managed()
 
         def _clear_items(self, source_col_name):
             # source_col_name: the PK colname in join_table for the source object
+            dispatcher.send(signal=signals.m2m_clear_items, sender=self.model,
+                            instance=self.instance, field_name=self.field_name)
             cursor = connection.cursor()
             cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
                 (self.join_table, source_col_name),
@@ -405,7 +419,8 @@
             symmetrical=False,
             join_table=qn(self.related.field.m2m_db_table()),
             source_col_name=qn(self.related.field.m2m_reverse_name()),
-            target_col_name=qn(self.related.field.m2m_column_name())
+            target_col_name=qn(self.related.field.m2m_column_name()),
+            field_name=self.related.field.name
         )
 
         return manager
@@ -446,7 +461,8 @@
             symmetrical=(self.field.rel.symmetrical and instance.__class__ == rel_model),
             join_table=qn(self.field.m2m_db_table()),
             source_col_name=qn(self.field.m2m_column_name()),
-            target_col_name=qn(self.field.m2m_reverse_name())
+            target_col_name=qn(self.field.m2m_reverse_name()),
+            field_name=self.field.name
         )
 
         return manager
Index: db/models/signals.py
===================================================================
--- db/models/signals.py	(revision 7049)
+++ db/models/signals.py	(working copy)
@@ -10,3 +10,7 @@
 post_delete = object()
 
 post_syncdb = object()
+
+m2m_add_items = object()
+m2m_remove_items = object()
+m2m_clear_items = object()
