Ruby-on-rails 加入范围:has_many:通过关联

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5856838/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 01:03:46  来源:igfitidea点击:

Scope with join on :has_many :through association

ruby-on-railsruby-on-rails-3

提问by deefour

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

hiddenis an extra boolean column within the m2m association table. Given some Usersinstance current_user, I want to do

hidden是 m2m 关联表中的额外布尔列。鉴于某些Users情况current_user,我想做

current_user.meetings.visible

which will retrieve a collection of Meetingsfor which the user is a participant where the hiddencolumn is false. The closest I have gotten is adding the following scope to the Meetingsclass

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

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

The scopedoes filter the Meetingsagainst the MeetingParticipationstable, however there is no join/condition against the MeetingParticipationstable related to current_user.

scope不过滤MeetingsMeetingParticipations表,但有没有加入/条件对MeetingParticipations相关表格current_user

The issue with this is, if current_userand another_userare both participants for some Meetingsinstance, a Meetingsrecord in the result set will be returned for each participant that has hiddenset to false. If current_userhas trueset for hiddenfor all Meetings, if another_useris a participant in any of those same Meetings with hiddenset to false, those Meetingswill appear in the Meetings.visibleresult set.

这样做的问题是,如果current_useranother_user都是某些Meetings实例的参与者,Meetings结果集中的记录将为每个hidden设置为 的参与者返回false。如果current_user已为所有人true设置为,如果是任何相同会议的参与者且设置为,则这些会议将出现在结果集中。hiddenMeetingsanother_userhiddenfalseMeetingsMeetings.visible

Is it possible to have a scope as I've mentioned above which will properly join on the Userinstance? If not, can someone recommend a solution to this?

是否有可能像我上面提到的那样有一个可以正确加入User实例的范围?如果没有,有人可以推荐解决方案吗?

回答by Andrei Erdoss

This is my solution for your problem:

这是我针对您的问题的解决方案:

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

@user.meetings.visible

回答by Paul Pettengill

In Rails 4, you can specify the scope originally defined in the child object in the association itself. Short: you don't have to know the internals of the MeetingParticipation model within the User model.

在 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

This would allow you to do: user1.visible_meetingsand user2.visible_meetingswith different result sets

这将允许您执行以下操作:user1.visible_meetingsuser2.visible_meetings使用不同的结果集

回答by woto

current_user.meetings.merge(MeetingParticipations.visible)

回答by thisismydesign

The clean, associations way to do it is:

干净的关联方法是:

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

To put it in more generic terms: if you have a chained has_manyassociation you can scope the intermediate (through) association via merging the scope. Probably requires Rails 4+.

用更通用的术语来说:如果您有一个链式has_many关联,您可以through通过合并范围来确定中间 ( ) 关联的范围。可能需要 Rails 4+。

Otherwise this would have to be done via creating a (probably unwanted) intermediate scoped association as seen in @Paul Pettengill's answer.

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

回答by Abass Sesay

I know this question was answered a while back but I just encountered a similar issue and was looking around for the best way to handle this. The accepted solution is very simple but I think it would be cleaner by moving the scope of the association from Usersto Meetingas should below

我知道这个问题不久前得到了回答,但我刚刚遇到了一个类似的问题,并且正在四处寻找处理这个问题的最佳方法。接受的解决方案非常简单,但我认为通过将关联范围从UsersMeeting如下所示,它会更清晰

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

With this, you are able to call current_user.meetings.hidden

有了这个,你可以打电话 current_user.meetings.hidden

By design, the meeting now dictates what makes it hidden/visible.

按照设计,会议现在决定了是什么让它隐藏/可见。

回答by Mirror318

Here's a one liner:

这是一个单班轮:

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

This is great because you can make a scope out of it, a function out of it, or simply call it anywhere. You can also add any more restrictions you want to the hash.

这很棒,因为您可以从中创建一个范围,一个函数,或者简单地在任何地方调用它。您还可以为哈希添加更多限制。

回答by dkniffin

You could also do:

你也可以这样做:

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

回答by moritz

It would seem to me that it is not sensible to use a scope on Meeting for your purpose. A meeting itself has no visibility, but the participation has. So I would suggest an extension on the association within User:

在我看来,将会议范围用于您的目的是不明智的。会议本身没有可见性,但参与具有。因此,我建议对 User 内的关联进行扩展:

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

I hope, this helps.

我希望这有帮助。