SQLAlchemy 单表的多对多关系
我在我的应用程序中设置了一个SQLAlchemy模型,目的是模仿Twitter上的“关注者”功能,也就是说,用户之间有一种多对多的关系(既可以关注别人,也可以被别人关注)。表的结构如下(sa是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)
)
不过,我在使用orm.mapper来创建这种关系时遇到了一些困难,因为这个中间表在两个方向上都指向同一个主表。我该如何将这种关系映射到ORM中呢?
2 个回答
24
你也可以用声明的方式来做这个。
下面是一个类似的例子,基于上面的代码,我确实使用了反向引用。
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")
10
在这种情况下,你需要明确写出 primaryjoin
和 secondaryjoin
的条件:
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),
),
},
)
我写了这个详细的示例,帮助你更好地理解 primaryjoin
和 secondaryjoin
参数的意思。当然,你可以用 backref
来简化它。
顺便说一下,你在关注表中不需要 id
列,应该使用复合主键。实际上,你应该为 follower_id
和 followee_id
这对组合定义唯一约束(可以作为主键或额外的唯一键)。