Relación SQLAlchemy de muchos a muchos en una sola mesa

Tengo un modelo SQLAlchemy configurado en mi aplicación que debería imitar la funcionalidad de los “seguidores” en Twitter, es decir. los usuarios tienen una relación de muchos a muchos entre sí (tanto seguidores como seguidores). Las tablas están estructuradas de la siguiente manera (sa es el módulo sqlalchemy):

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

Sin embargo, me he topado con un pequeño obstáculo al tratar de usar orm.mapper para crear esta relación, ya que la tabla secundaria se refiere a la misma tabla principal en ambas direcciones. ¿Cómo haría para mapear esta relación con el ORM?

En este caso, debe escribir primaryjoin secondaryjoin condiciones de primaryjoin y secondaryjoin en este 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), ), }, ) 

He escrito este ejemplo detallado para ayudarlo a comprender mejor lo que primaryjoin secondaryjoin parámetros de primaryjoin y secondaryjoin . Claro, puedes hacerlo clasificador con backref .

Por cierto, no necesita la columna de id en la siguiente tabla, use la clave primaria compuesta en su lugar. De hecho, debe definir la restricción única de los pares follower_id y followee_id todos modos (ya sea como clave única primaria o adicional).

También puedes hacer esto declarativamente.

Aquí hay un ejemplo similar basado en el código anterior, yo uso el 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")