2011-09-02 63 views
1

更新:這可能是不可行的。 See this包含一個關聯,如果它存在於rails查詢中

TLDR:您如何有條件地加載關聯(比如只加載當前用戶的關聯),同時還包括根本沒有關聯的記錄?

Rails 3.1,這裏大概是我正在使用的模型。

class User 
    has_many :subscriptions 
    has_many :collections, :through => :subscriptions 
end 

class Collection 
    has_many :things 
end 

class Thing 
    has_many :user_thing_states, :dependent => :destroy 
    belongs_to :collection 
end 

class Subscription 
    belongs_to :user 
    belongs_to :collection 
end 

class UserThingState 
    belongs_to :user 
    belongs_to :thing 
end 

有很多收藏品有很多東西。用戶訂閱許多集合,因此他們訂閱很多東西。用戶具有與事物相關的狀態,但不一定,並且仍然訂閱事物,即使它們沒有發生狀態。當用戶訂閱一個集合及其關聯的東西時,不會爲每一件事情(可能會有數百個)生成一個狀態。相反,狀態是在用戶首次與給定事物交互時生成的。現在,問題是:我想在加載狀態所在的每個事物的用戶狀態時選擇用戶的所有訂閱內容。

從概念上講,這並不難。作爲參考,SQL將爲我提供所需的數據是:

SELECT things.*, user_thing_states.* FROM things 
# Next line gets me all things subscribed to 
INNER JOIN subscriptions as subs ON things.collection_id = subs.collection_id AND subs.user_id = :user_id 
# Next line pulls in the state data for the user 
LEFT JOIN user_thing_states as uts ON things.id = uts.thing_id AND uqs.user_id = :user_id 

我只是不知道如何將它拼湊在一起。 Thing課堂會發生什麼? Thing.includes(:user_thing_states)將加載所有用戶的所有狀態,並且看起來像是唯一的工具。我需要的是這樣的,但我不知道如何(或者如果可能的話):

class Thing 
    has_many :user_thing_states 
    delegates :some_state_property, :to => :state, :allow_nil => true 

    def state 
     # There should be only one user_thing_state if the include is correct, state method to access it. 
     self.user_thing_states.first 
    end 
    end 

我需要這樣的東西:

Thing.includes(:user_question_states, **where 'user_question_state.user_id => :user_id**).by_collections(user.collections) 

然後,我可以做

things = User.things_subscribed_to 
things.first.some_state_property # the property of the state loaded for the current user. 

回答

0

回答我自己的問題兩次...有點尷尬,但無論如何。

Rails似乎沒有讓你指定include()語句的附加條件。如果是這樣,我以前的答案會起作用 - 您可以在includes()語句中添加一個附加條件,以使where條件正確工作。爲了解決這個問題,我們就需要得到包括()使用類似下面的SQL(獲取「AND」條件的問題):我採取這種現在

LEFT JOIN user_thing_states as uts ON things.id = uts.thing_id AND uqs.user_id = :user_id 

這是一個有點可怕。

class User 
    ... 

    def subscribed_things 
    self.subscribed_things_with_state + self.subscribed_things_with_no_state 
    end 

    def subscribed_things_with_state 
    self.things.includes(:user_thing_states).by_subscribed_collections(self).all 
    end 

    def subscribed_things_with_no_state 
    Thing.with_no_state().by_subscribed_collections(self).all 
    end 

end 
2

你不」不需要做任何事情。

class User 
    has_many :user_thing_states 
    has_many :things, :through => :user_thing_states 
end 

# All Users w/ Things eager loaded through States association 
User.all.includes(:things) 

# Lookup specific user, Load all States w/ Things (if they exist for that user) 
user = User.find_by_login 'bob' 
user.user_thing_states.all(:include => :things) 

使用includes()已經加載關聯的對象,如果它們存在。

沒有必要爲沒有關聯對象的用戶進行任何過濾或添加額外行爲。

+0

問題是模型的其他部分是類別和訂閱:用戶訂閱的類別和類別包含很多東西。所以問題是用戶可能通過訂閱與很多東西相關聯,並且他們訂閱的東西集大於他們所處的東西集,因爲只有當用戶第一次與東西交互時才創建狀態。所以我在尋找**用戶訂閱的東西是否有用戶的狀態,但包括用戶的狀態對象(如果存在)** –

+0

您沒有定義任何的概念訂閱作爲模型或模式。我覺得你讓這件事比需要的複雜得多。添加一個如何過濾這個用戶的例子,因爲你似乎偶然發現了這個。 – Winfield

+0

爲了清晰起見,我重寫了原始問題(爲了簡潔起見,我省略了這些問題使其更加混亂),並在解決問題的同時解決問題。去搞清楚。儘管如此,你的更新仍然存在同樣的問題:用戶可以訂閱比他們所擁有的更多的東西,我希望所有這些東西。感謝您的幫助,但通過用戶考慮是很有幫助的。 –

0

*不,不解決它:(Here's a treatment of the specific issue I seem to have hit.

想我已經知道了,比預期的更容易:

class Thing 
    has_many :user_thing_states 
    delegates :some_state_property, :to => :state, :allow_nil => true 

    scope :with_user_state, lambda { |user| 
    includes(:user_thing_states).where('user_thing_states.user_id = :user_id 
             OR user_thing_states.user_id IS NULL', 
             {:user_id => user.id}) } 

    def state 
    self.user_thing_states.first 
    end 
end 

所以:

Thing.with_user_state(current_user).all 

將加載所有的事情並且每個事物只有一個user_question_state可以通過狀態訪問,並且不會排除無狀態的東西。