加入的范围:has_many:通过关联

时间:2011-05-02 11:56:55

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

class Users < ActiveRecord::Base
  has_many :meetings, :through => :meeting_participations
  has_many :meeting_participations
end

class Meetings < ActiveRecord::Base
  has_many :users, :through => :meeting_participations
  has_many :meeting_participations
end

class MeetingParticipations < ActiveRecord::Base
  belongs_to :user
  belongs_to :meeting

  scope :hidden, where(:hidden => true)
  scope :visible, where(:hidden => false)
end

hidden是m2m关联表中的一个额外布尔列。给定一些Users实例current_user,我想做

current_user.meetings.visible

将检索Meetings的集合,其中用户是hidden列为false列的参与者。我得到的最接近的是将以下范围添加到Meetings

scope :visible, joins(:meeting_participations) & MeetingParticipation.visible

scope确实会针对Meetings表过滤MeetingParticipations,但是与MeetingParticipations相关的current_user表没有加入/条件。< / p>

问题是,如果current_useranother_user都是某个Meetings实例的参与者,则会为每个参与者返回结果集中的Meetings条记录已将hidden设置为false的人。如果current_user为所有true设置了hidden Meetings,则another_userhidden设置为false的任何相同会议的参与者Meetings Meetings.visible User将显示在{{1}}结果集中。

是否可以使用上面提到的范围,它将正确加入{{1}}实例?如果没有,有人可以推荐一个解决方案吗?

8 个答案:

答案 0 :(得分:82)

这是我解决您问题的方法:

class User < ActiveRecord::Base
  has_many :meeting_participations
  has_many :meetings, :through => :meeting_participations do
   def visible
     where("meeting_participations.visible = ?", true)
   end
  end
end

@user.meetings.visible

答案 1 :(得分:59)

在Rails 4中,您可以在关联本身中指定最初在子对象中定义的范围。简而言之:您无需了解User模型中MeetingParticipation模型的内部结构。

class User < ActiveRecord::Base
  has_many :meeting_participations
  has_many :meetings, :through => :meeting_participations
  has_many :visible_participations, -> { visible }, :class_name => 'MeetingParticipation'
  has_many :visible_meetings, :source => :meeting, :through => :visible_participations
end

class Meeting < ActiveRecord::Base
  has_many :meeting_participations
  has_many :users, :through => :meeting_participations
end

class MeetingParticipation < ActiveRecord::Base
  belongs_to :user
  belongs_to :meeting

  scope :hidden, -> { where(:hidden => true) }
  scope :visible, -> { where(:hidden => false) }
end

这样您就可以执行:user1.visible_meetingsuser2.visible_meetings具有不同的结果集

答案 2 :(得分:7)

def belongs_to_user_in_pending_state(user)
  if self.user_items.pending.select {|s| s.user == user}.count > 0 
    return true
  else
    return false
  end
end

答案 3 :(得分:1)

这是一个班轮:

Meeting.joins(:meeting_participations).where(meeting_participation: { hidden: false, user_id: current_user.id })

这很棒,因为你可以用它制作一个范围,一个函数,或者只是在任何地方调用它。您还可以向哈希添加任何您想要的限制。

答案 4 :(得分:1)

干净,关联的方法是:

has_many :visible_meetings, -> { merge(MeetingParticipations.visible) },
  :source => :meeting, :through => :meeting_participations

用更笼统的术语来说:如果您有一个链接的has_many关联,则可以通过合并作用域来定义中间(through)关联。可能需要Rails 4 +。

否则,必须通过创建(可能是不需要的)中间作用域关联来完成,如@Paul Pettengill的回答所示。

答案 5 :(得分:0)

我知道这个问题已在不久前得到解答,但我刚刚遇到了类似的问题,并且正在寻找解决该问题的最佳方法。公认的解决方案非常简单,但我认为将关联的范围从Users移到Meeting会更清洁,如下所述

class Users < ActiveRecord::Base
  has_many :meetings, :through => :meeting_participations
  has_many :meeting_participations
end

class Meetings < ActiveRecord::Base
  has_many :users, :through => :meeting_participations
  has_many :meeting_participations
  scope :hidden, -> { where('meeting_participations.hidden = ?', true) }
  scope :visible, -> { where('meeting_participations.hidden = ?', false) }
end

class MeetingParticipations < ActiveRecord::Base
  belongs_to :user
  belongs_to :meeting

  scope :hidden, where(:hidden => true)
  scope :visible, where(:hidden => false)
end

这样,您就可以呼叫current_user.meetings.hidden

根据设计,会议现在指示隐藏/可见的原因。

答案 6 :(得分:-3)

在我看来,为了您的目的而在会议上使用范围是不明智的。会议本身没有可见性,但参与有。所以我建议对用户内的关联进行扩展:

class User < ActiveRecord::Base
  has_many :meetings, :through => :meeting_participations do
    def visible
      ids = MeetingParticipation.
        select(:meeting_id).
        where(:user_id => proxy_owner.id, :visible => true).
        map{|p| p.meeting_id}
      proxy_target.where("id IN (?)", ids)
    end
  end
  ...
end

我希望,这有帮助。

答案 7 :(得分:-3)

你也可以这样做:

current_user.meeting_participations.visible.map(&:meeting)