SQLAlchemy:选择[..]中包含标签的所有帖子(多对多)

时间:2014-02-17 21:53:15

标签: sql sqlalchemy flask flask-sqlalchemy

我有用户,兴趣和活动。 用户拥有(多对多)兴趣。事件有(多对多)兴趣。这就是为什么我有两个“中间”表:user_to_interest和event_to_interest。

我想以某种方式从用户兴趣列表中选择所有感兴趣的事件(换句话说,所有具有标签IN [1,144,4324]的事件)。

在SQL中我会这样做〜就像这样:

SELECT DISTINCT event.name FROM event JOIN event_to_interest ON event.id = event_to_interest.event_id WHERE event_to_interest.interest_id IN (10, 144, 432)

我应该如何通过SQLAlchemy做到这一点? (如果需要,我正在使用Flask-SQLAlchemy)

1 个答案:

答案 0 :(得分:3)

假设你有一个(简化的)模型,如下所示:

user_to_interest = Table('user_to_interest', Base.metadata,
    Column('id', Integer, primary_key=True),
    Column('user_id', Integer, ForeignKey('user.id')),
    Column('interest_id', Integer, ForeignKey('interest.id'))
    )

event_to_interest = Table('event_to_interest', Base.metadata,
    Column('id', Integer, primary_key=True),
    Column('event_id', Integer, ForeignKey('event.id')),
    Column('interest_id', Integer, ForeignKey('interest.id'))
    )

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String)

class Event(Base):
    __tablename__ = 'event'
    id = Column(Integer, primary_key=True)
    name = Column(String)

class Interest(Base):
    __tablename__ = 'interest'
    id = Column(Integer, primary_key=True)
    name = Column(String)

    users = relationship(User, secondary=user_to_interest, backref="interests")
    events = relationship(Event, secondary=event_to_interest, backref="interests")

版本1:您应该可以对interest_id列表进行简单查询,这将基本生成您想要的SQL语句:

interest_ids = [10, 144, 432]
query = session.query(Event.name)
query = query.join(event_to_interest, event_to_interest.c.event_id == Event.id)
query = query.filter(event_to_interest.c.interest_id.in_(interest_ids))

但是,如果列表中存在两个或多个兴趣的事件,则查询将多次返回相同的Event.name。您可以使用distinct来处理它:query = session.query(Event.name.distinct())

版本-2:或者,您可以仅使用关系来执行此操作,这将使用带有SQL子句的子查询生成不同的EXISTS,但在语义上它应该是同样的:

query = session.query(Event.name)
query = query.filter(Event.interests.any(Interest.id.in_(interest_ids)))

此版本没有重复问题。

但是,我会退后一步,并假设你为特定用户获得interest_ids,并会创建一个适用于user_id(或User.id)<的查询/ p>

最终版本:两次使用any

def get_events_for_user(user_id):
    #query = session.query(Event.name)
    query = session.query(Event) # @note: I assume name is not enough
    query = query.filter(Event.interests.any(Interest.users.any(User.id == user_id)))
    return query.all()

可以说这会产生不那么漂亮的SQL语句,但这正是使用隐藏实现细节的SQLAlchemy的美妙之处。


加分:您可能实际上希望为具有更多重叠兴趣的事件提供更高优先级。在这种情况下,以下内容可能有所帮助:

query = session.query(Event, func.count('*').label("num_interests"))
query = query.join(Interest, Event.interests)
query = query.join(User, Interest.users)
query = query.filter(User.id == user_id)
query = query.group_by(Event)
# first order by overlaping interests, then also by event.date
query = query.order_by(func.count('*').label("num_interests").desc())
#query = query.order_by(Event.date)