title: Place SQLAlchemy event listener on many-to-many table
body: <p>I'm trying to detect when a many-to-many relationship is created or deleted, but I'm having trouble figuring out the right event to listen for.</p>
  
  <p>Let's say I have a <code>User</code> model and a <code>Team</code> model, and I'm using a membership table to define which users belong to which teams. Here's a simple schema (using Flask-SQLAlchemy's base model):</p>
  
  <pre><code>membership_table = db.Table('membership', db.metadata,
      db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
      db.Column('team_id', db.Integer, db.ForeignKey('team.id')),
      db.PrimaryKeyConstraint('user_id', 'team_id'))
  
  class User(db.Model):
      __tablename__ = 'user'
      id = db.Column(db.Integer, primary_key=True)
      name = db.Column(db.String(64))
      teams = db.relationship('Team', secondary=membership_table, backref='users')
  
  class Team(db.Model):
      __tablename__ = 'team'
      id = db.Column(db.Integer, primary_key=True)
      name = db.Column(db.String(64))
  </code></pre>
  
  <p>What I want to do is detect when someone joins or leaves the team, and trigger an event that can do something with this information (e.g. send a notification somewhere: "Dave joined the Yankees"; "Sarah left the Mets").</p>
  
  <p>I tried first attaching <code>after_insert</code> and <code>after_delete</code> events directly to the membership table, but this simply failed with an <code>AttributeError: after_delete</code> exception, which was more or less what I expected given that it's not a model like the others. </p>
  
  <p>I tried attaching a <code>set</code> listener to <code>User.teams</code>:</p>
  
  <pre><code>@event.listens_for(User.teams, 'set')
  def membership_updated(target, value, oldvalue, initiator):
      # compare `oldvalue` to `newvalue` to determine membership change
  </code></pre>
  
  <p>However this event is simply never triggered when I add or remove users from a team</p>
  
  <p>I tried simply listen for updates to <code>Team</code>:</p>
  
  <pre><code>@event.listens_for(Team, 'after_update')
  def test(mapper, connection, target):
      # check current membership via `target.users`
  </code></pre>
  
  <p>This is correctly triggered when membership changes, but the problem is I can't really reason about what's happening. Maybe someone simply edited the team name, and the membership hasn't changed at all. And if it has changed, how? Was someone added or deleted?</p>
  
  <p>I'm a bit stuck on where to go next, or how else to get the necessary information.</p>