2017-09-04 7 views
0

J'essaie de détecter lorsqu'une relation plusieurs-à-plusieurs est créée ou supprimée, mais j'ai de la difficulté à trouver le bon événement à écouter. Disons que j'ai un modèle User et un modèle Team, et j'utilise une table d'appartenance pour définir quels utilisateurs appartiennent à quelles équipes. Voici un schéma simple (en utilisant le modèle de base de Flask-SQLAlchemy):Placer un écouteur d'événement SQLAlchemy sur une table plusieurs-à-plusieurs

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)) 

Ce que je veux faire est de détecter quand quelqu'un rejoint ou quitte l'équipe, et de déclencher un événement qui peut faire quelque chose avec ces informations (par exemple envoyer un notification quelque part: "Dave a rejoint les Yankees", "Sarah a quitté les Mets").

J'ai essayé d'abord fixer des événements after_insert et after_delete directement à la table des membres, mais tout simplement échoué à l'exception AttributeError: after_delete, qui était plus ou moins ce que j'attendais étant donné que ce n'est pas un modèle comme les autres.

J'ai essayé attacher un auditeur set-User.teams:

@event.listens_for(User.teams, 'set') 
def membership_updated(target, value, oldvalue, initiator): 
    # compare `oldvalue` to `newvalue` to determine membership change 

Cependant cet événement est tout simplement jamais déclenché quand j'ajouter ou supprimer des utilisateurs d'une équipe

J'ai essayé simplement écouter les mises à jour de Team:

@event.listens_for(Team, 'after_update') 
def test(mapper, connection, target): 
    # check current membership via `target.users` 

Ceci est correctement déclenché lorsque l'adhésion change, mais le problème est que je ne peux pas vraiment Ce qui se passe Peut-être que quelqu'un a simplement édité le nom de l'équipe, et l'adhésion n'a pas changé du tout. Et si ça a changé, comment? Quelqu'un a-t-il été ajouté ou supprimé?

Je suis un peu coincé sur où aller, ou comment obtenir les informations nécessaires.

Répondre

1

Afin de « détecter quand quelqu'un rejoint ou quitte l'équipe » écouter les événements append et remove sur Team.users relation:

@event.listens_for(Team.users, 'append') 
def receive_team_users_append(target, value, initiator): 
    print(value.name, 'added to team', target.name) 

@event.listens_for(Team.users, 'remove') 
def receive_team_users_append(target, value, initiator): 
    print(value.name, 'removed from team', target.name) 

Les gestionnaires d'événements se déclenche également si vous ajoutez une équipe aux équipes d'un utilisateur collection, grâce à how backref/back_populates mirror operations entre les 2 propriétés de la relation.

+0

Brillant! Je ne savais même pas à propos de ces événements, exactement ce dont j'avais besoin. Je devrais noter que j'ai obtenu l'exception 'AttributeError: l'objet type 'Team' n'a pas d'attribut 'users'' quand j'ai écouté sur' Team.users', donc j'ai dû faire l'inverse et écouter sur 'User.teams' (ceci est logique je suppose, puisque j'ai défini les deux côtés de la relation à l'intérieur du modèle de l'utilisateur). J'ai testé cela dans mon code et ça fonctionne parfaitement. Merci encore de m'avoir appris quelque chose de nouveau. – daveruinseverything

+1

Les mappeurs sont complètement initialisés la première fois que vous les utilisez, donc si vous essayiez d'utiliser 'Team.users' avant, il ne pourrait pas encore exister car [' Team' est résolu paresseusement] (http: //docs.sqlalchemy .org/fr/latest/orm/relation_api.html # sqlalchemy.orm.relationship.params.argument) car il est transmis en tant que chaîne évaluable Python à la relation. J'espère que cela a du sens. –