2011-09-02 65 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可以通过状态访问,并且不会排除无状态的东西。