2009-12-11 6 views
10

Ho un modello SQLAlchemy impostato nella mia applicazione che dovrebbe imitare la funzionalità di "follower" su Twitter, es. gli utenti hanno una relazione molti-a-molti tra loro (seguaci e seguaci). Le tabelle sono strutturati come segue (sa è il modulo sqlalchemy):SQLAlchemy Relazione molti-a-molti su una tabella singola

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

ho incontrato un po 'di un posto di blocco però, cercando di utilizzare orm.mapper per creare questo rapporto, dal momento che la tabella secondaria rimanda alla stessa tabella primaria in entrambe le direzioni. Come potrei fare per mappare questa relazione con l'ORM?

risposta

6

dovete scrivere primaryjoin e secondaryjoin condizioni esplicitamente in questo caso:

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

Ho scritto questo campione verbose per aiutarvi a capire meglio cosa primaryjoin e secondaryjoin parametri significano. Certo, puoi farlo sorter con backref.

BTW, non è necessaria la colonna id nella tabella seguente, utilizzare invece la chiave primaria composita. In effetti, è necessario definire un vincolo univoco della coppia follower_id e followee_id in ogni caso (chiave primaria o chiave univoca aggiuntiva).

+0

Grazie, ha funzionato perfettamente. Intendevi che la tabella seguente non richiede una colonna ID e può utilizzare un PK composito? Non vedo come potrebbe funzionare con la tabella degli utenti. – Travis

+0

Sì, è stato un errore. Intendevo la tabella seguente. –

+0

Mi sono imbattuto in questo e ho dovuto farlo in modo dichiarativo, ecco l'equivalente per i cercatori futuri. –

14

È anche possibile farlo in modo dichiarativo.

Ecco un esempio simile basato sul codice di cui sopra, io uso il 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

Per me, ho dovuto aggiungere l'analogico di 'foreign_keys = [VolumeRelationship.c.VolumeID, VolumeRelationship.c.ParentID])' a 'Volume.parents', altrimenti ho avuto 'NoReferencedTableError'. –