2014-02-17 90 views
0

我有用户,兴趣和事件。 用户拥有(多对多)兴趣。活动有(多对多)利益。这就是为什么我有两个“中间”表:user_to_interest和event_to_interest。SQLAlchemy:选择所有包含[..]标签的帖子(多对多)

我想以某种方式从用户的兴趣列表中选择所有具有兴趣的事件(换句话说,所有具有标记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的? (我用的烧瓶的SQLAlchemy如果必要的话)

+0

你确定你的'WHERE'子句检查'event_to_interest.id'而不是'event_to_interest.interest_id'吗? – van

+0

@van,对。必须有'interest_id'。 –

回答

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 s,这将产生基本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)) 

但是,如果存在具有两个或两个以上列表中的利益的事件,查询将返回SAM e Event.name多次。 query = session.query(Event.name.distinct())

版本2:您可以通过使用distinct的工作,虽然,它周围或者,您也可以做到这一点只用关系,这将使用子查询与EXISTS条款产生不同的SQL,但语义应该是相同:

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

此版本没有重复的问题。

不过,我会去退一万步,并假设你得到interest_ids为特定的用户,并会创建一个user_id(或User.id

最终版本工作的查询:使用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() 

人们可以agrue,这造成不是很漂亮的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) 
+0

哇!非常感谢您提供这样详细的答案!你必须是一个真正的SQL-alchemic :) –

+0

你刚刚救了我的一天,谢谢! – ncrocfer

相关问题