如何在ActiveRecord关联方法中访问原始模型

时间:2011-11-04 02:45:26

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord associations

我一直在使用ActiveRecord关联方法(例如在这篇文章Ryan's Scraps中)来执行一些更复杂的二级关联,同时最大限度地减少查询次数。例如,我使用以下关联从用户所关注的每个人中提取所有活动(以下是仅包含user_id和followed_user_id的模型):

class User < ActiveRecord::Base
 <other associations, code, etc>
 has_many :follows do
 def activities(reload=false)
   @followed_activities = nil if reload
   @followed_activities ||= Activity.includes(:user, :comments, :comment_users).where(:user_id.in => self.collect {|e| e.followed_user_id}).order('activities.created_at DESC')
 end
 <.....>
end

这种方法非常有效,没有抱怨。但是,客户端已请求包含用户自身的活动(follow_users的活动+ current_user的活动)。

我意识到这可能是ruby 101,但我无法弄清楚如何从父(User)模型访问User.id。正如您在代码中看到的那样,self引用了以下关系,并从下表中返回匹配行的列表。

我有时通过更改以下行来使其工作:

@followed_activities ||= Activity.includes(:user, :comments, :comment_users).where(:user_id.in => (self.collect {|e| e.followed_user_id}) << self.first.user_id).order('activities.created_at DESC')

然而,这a)感觉非常hackish,并且b)在没有任何跟随记录时引发异常(对于新用户总是如此)。我怎样才能获得用户ID?

1 个答案:

答案 0 :(得分:2)

the episode 215 Asciicast所示,你可以使用&运算符合并两个关系,所以这样的东西应该有效:

@followed_activities    ||= Activity
                             .includes(:user, :comments, :comment_users)
                             .where(:user_id.in => (self.collect {|e| e.followed_user_id})
@current_users_activity ||= Activity
                             .includes(:user, :comments, :comment_users)
                             .where(:user_id.in => [self.first.user_id] )
@all_activities         ||= (@followed_activities & @current_users_activity).order('activities.created_at DESC')

编辑

注意:在rails 3.1中,&运算符已弃用,请改用Relation#merge

做了一些有趣的研究。这绝对不是“红宝石101”!

实际上,代码中的self确实引用了User对象及其关联的Follow记录之间的Association Proxy。当你像这样扩展一个关联时,你扩展了代理(因为我几乎不理解它,该块是instance_eval d代理类。)

在rails&gt; = 3.1中,有一种方法可以在您的块中获取您的用户ID(请参阅this doc,“关联扩展”部分):

proxy_association.owner.id

所以你应该能够做到:

class User < ActiveRecord::Base

  has_many :follows do

    def activities( reload = false )
      @followed_activities = nil if reload
      @followed_activities ||= begin
        Activity
          .includes( :user, :comments, :comment_users )
          .where( :user_id.in => [proxy_association.owner.id, *map( &:followed_user_id ) ])
          .order( 'activities.created_at DESC' )
      end
    end

  end

end