鉴于用户和他们之间的直接关系:
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
username = Column(String(30), nullable=False)
follows = relationship(
'User', secondary='following',
primaryjoin=(Following.follower_id == id),
secondaryjoin=(Following.followee_id == id)
)
followed_by = relationship(
'User', secondary='following',
primaryjoin=(Following.followee_id == id),
secondaryjoin=(Following.follower_id == id)
)
class Following(Base):
__tablename__ = 'following'
follower_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
followee_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
我需要使用SQLAlchemy来选择三位一体的用户,例如:
A follows B
B follows A
A follows C
C follows B
我无法弄清楚如何使用exists()
来选择双向关系。
答案 0 :(得分:0)
我认为在所需的关系(表JOIN
)上使用多个内部Following
,您可以以一种相当直接的方式实现这一点:
# create aliases for each pair of relations that must exist
AB = aliased(Following)
BA = aliased(Following)
AC = aliased(Following)
CB = aliased(Following)
CA = aliased(Following)
triads = (
session.query(
AB.follower_id.label("a_id"),
AB.followee_id.label("b_id"),
AC.followee_id.label("c_id"),
)
# .select_from(AB) # @note: this is implied, so not really required
.join(BA, and_(AB.followee_id == BA.follower_id,
AB.follower_id == BA.followee_id))
.join(AC, AB.follower_id == AC.follower_id)
.join(CB, and_(AC.followee_id == CB.follower_id,
AB.followee_id == CB.followee_id))
# exclude C following A using LEFT JOIN + WHERE IS NULL)
.outerjoin(CA, and_(AC.followee_id == CA.follower_id,
AC.follower_id == CA.followee_id))
.filter(CA.follower_id == None)
)
for a, b, c in triads:
print(a, b, c)
如果User
可以跟随他/她自己,这可能不会那么强大,在这种情况下,可以用额外的where
条款排除这种情况。
选项-2:另一种干净(非常易读)的方法是确保存在实际关系,但下面代码生成的SQL
语句可能是一种过度杀伤力达到预期的效果:
# create aliases for each assumed user (A, B, C)
A = aliased(User)
B = aliased(User)
C = aliased(User)
# also aliases to navigate relationship and check same user
Aa = aliased(User)
Bb = aliased(User)
triads = (
session.query(A, B, C)
.join(B, A.follows) # A follows B
.join(Aa, B.follows).filter(Aa.id == A.id) # B follows Aa (same as A)
.join(C, A.follows) # A follows C
.join(Bb, C.follows).filter(Bb.id == B.id) # C follows Bb (same as B)
.filter(~C.follows.any(User.id == A.id)) # exclude C following A
)
for a, b, c in triads:
print(a, b, c)