2009-12-11 4 views
10

J'ai un modèle SQLAlchemy mis en place dans mon application qui devrait imiter la fonctionnalité de "followers" sur Twitter, ie. les utilisateurs ont une relation plusieurs-à-plusieurs entre eux (les suiveurs et les suivants). Les tables sont structurées comme suit (sa est le module sqlalchemy):SQLAlchemy relation plusieurs-à-plusieurs sur une seule table

t_users = sa.Table("users", meta.metadata, 
    sa.Column("id", sa.types.Integer, primary_key=True), 
    sa.Column("email", sa.types.String(320), unique=True, nullable=False), 
    ...etc... 
    ) 

t_follows = sa.Table("follows", meta.metadata, 
    sa.Column("id", sa.types.Integer, primary_key=True), 
    sa.Column("follower_id", sa.types.Integer, sa.ForeignKey('users.id'), nullable=False), 
    sa.Column("followee_id", sa.types.Integer, sa.ForeignKey('users.id'), nullable=False) 
    ) 

J'ai rencontré un peu d'un barrage routier cependant, en essayant d'utiliser orm.mapper pour créer cette relation, puisque la table secondaire renvoie à la même table primaire dans les deux directions. Comment pourrais-je cartographier cette relation avec l'ORM?

Répondre

6

Vous devez écrire primaryjoin et secondaryjoin conditions explicitement dans ce cas:

mapper(
    User, t_users, 
    properties={ 
     'followers': relation(
      User, 
      secondary=t_follows, 
      primaryjoin=(t_follows.c.followee_id==t_users.c.id), 
      secondaryjoin=(t_follows.c.follower_id==t_users.c.id), 
     ), 
     'followees': relation(
      User, 
      secondary=t_follows, 
      primaryjoin=(t_follows.c.follower_id==t_users.c.id), 
      secondaryjoin=(t_follows.c.followee_id==t_users.c.id), 
     ), 
    }, 
) 

J'ai écrit cet exemple verbeux pour vous aider à mieux comprendre ce que primaryjoin et secondaryjoin paramètres signifient. Bien sûr, vous pouvez le faire trier avec backref.

BTW, vous n'avez pas besoin de colonne id dans la table suit, utilisez plutôt la clé primaire composite. En fait, vous devez définir la contrainte unique follower_id et la paire followee_id de toute façon (soit comme clé unique primaire ou supplémentaire).

+0

Merci, cela a parfaitement fonctionné. Voulez-vous dire que la table suivante ne nécessite pas de colonne ID et peut utiliser un PK composite? Je ne vois pas comment cela pourrait fonctionner avec la table des utilisateurs. – Travis

+0

Oui, c'était une erreur. Je voulais dire suit la table. –

+0

Je me suis heurté à cela et j'ai dû le faire de manière déclarative, voici l'équivalent pour les futurs découvreurs. –

14

Vous pouvez également le faire de manière déclarative.

Voici un exemple similaire basé sur le code ci-dessus, j'utilise le backref.

VolumeRelationship = Table(
    'VolumeRelationship', Base.metadata, 
    Column('ParentID', Integer, ForeignKey('Volumes.ID')), 
    Column('VolumeID', Integer, ForeignKey('Volumes.ID')) 
    ) 

class Volume(Base): 
    """ Volume Object """ 
    __tablename__ = "Volumes" 

    id = Column('ID', Integer, primary_key=True, nullable=False) 
    type = Column('Type', String(25)) 
    name = Column('Name', String(25)) 
    poolid = Column('pool', Integer, ForeignKey('Pools.ID')) 
    parents = relation(
        'Volume',secondary=VolumeRelationship, 
        primaryjoin=VolumeRelationship.c.VolumeID==id, 
        secondaryjoin=VolumeRelationship.c.ParentID==id, 
        backref="children") 
+0

Pour moi, je devais ajouter l'analogue de 'foreign_keys = [VolumeRelationship.c.VolumeID, VolumeRelationship.c.ParentID])' à 'Volume.parents', sinon j'avais' NoReferencedTableError'. –

Questions connexes