2009-12-11 18 views
10

Tengo un modelo SQLAlchemy configurado en mi aplicación que debe imitar la funcionalidad de "seguidores" en Twitter, es decir. los usuarios tienen una relación de muchos a muchos entre sí (seguidores y seguidores). Las tablas tienen la siguiente estructura (sa es el módulo sqlalchemy):SQLAlchemy Relación muchos a muchos en una sola tabla

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

me he encontrado un poco de un control de carretera, sin embargo, tratar de utilizar orm.mapper para crear esta relación, ya que la tabla secundaria se refiere de nuevo a la misma tabla primaria en ambas direcciones. ¿Cómo haría para mapear esta relación con el ORM?

Respuesta

6

usted tiene que escribir primaryjoin y secondaryjoin condiciones explícitamente 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), 
     ), 
    }, 
) 

Yo he escrito esta muestra detallado para ayudarle a entender mejor lo que significan primaryjoin y secondaryjoin parámetros. Claro, puedes hacerlo clasificar con backref.

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

+0

Gracias, esto funcionó a la perfección. ¿Quiso decir que la tabla siguiente no requiere una columna ID y puede usar una PK compuesta? No veo cómo eso podría funcionar con la tabla de usuarios. – Travis

+0

Sí, fue un error. Me refiero a la siguiente tabla. –

+0

Me encontré con esto y tuve que hacerlo declarativamente, aquí está el equivalente para futuros buscadores. –

14

También puede hacerlo de forma declarativa.

Aquí hay un ejemplo similar basado en el código anterior, yo uso el respaldo.

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

Para mí, tuve que agregar un análogo de 'foreign_keys = [VolumeRelationship.c.VolumeID, VolumeRelationship.c.ParentID])' a 'Volume.parents', de lo contrario tuve' NoReferencedTableError'. –

Cuestiones relacionadas