2014-02-17 135 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

相關問題