Rails has_many:通过在联接模型中按附加属性查找


70

对于Ruby和Rails来说都是新手,但是我现在受过教育(这显然没有任何意义,哈哈)。

我有两个模型,Event和User通过表EventUser加入

class User < ActiveRecord::Base
  has_many :event_users
  has_many :events, :through => :event_users
end

class EventUser < ActiveRecord::Base
  belongs_to :event
  belongs_to :user

  #For clarity's sake, EventUser also has a boolean column "active", among others
end

class Event < ActiveRecord::Base
  has_many :event_users
  has_many :users, :through => :event_users
end

这个项目是一个日历,在其中我必须跟踪人们注册并为给定事件划掉他们的名字。我认为多对多是一种好方法,但是我不能做这样的事情:

u = User.find :first
active_events = u.events.find_by_active(true)

因为事件实际上并没有多余的数据,所以EventUser模型可以。虽然我可以做:

u = User.find :first
active_events = []
u.event_users.find_by_active(true).do |eu|
  active_events << eu.event
end

这似乎与“铁轨方式”背道而驰。谁能启发我,这在今晚(今早)困扰了我很长时间?


1
您也可以使用Gareth的建议来创建命名范围。
arjun

Answers:


123

如何在用户模型中添加类似内容?

has_many  :active_events, :through => :event_users, 
          :class_name => "Event", 
          :source => :event, 
          :conditions => ['event_users.active = ?',true]

之后,您只需调用以下命令就可以获取用户的活动事件:

User.first.active_events

1
我已经给您加了标签,但是如果早上没有得到更好的答复,那是您的。
Stefan Mai,

有关最新方法,请参见下面的答案
msanteler 2014年

9
谢谢,Rails 4的方式是:-> { where event_users: { active: true } }
Ivan Black

但是,没有机会通过event_users范围定义条件吗?喜欢scope :active, where(:active => true)吗?(使用Rails 3.2)
Augustin Riedinger 2014年

@IvanBlack您能解释一下适合has_many through:什么地方吗?您能否包括整个特定的Rails 4等效项?
Damien Roche 2014年

22

米兰·诺沃塔(Milano Novota)有一个很好的解决方案-但:conditions现在已过时,该:conditions => ['event_users.active = ?',true]位看起来似乎也不太合适。我喜欢这样的东西:

has_many :event_users
has_many :active_event_users, -> { where active: true }, class_name: 'EventUser'
has_many :active_events, :through => :active_event_users, class_name: 'Event', :source => :event

之后,您仍然可以通过调用以下命令来获取用户的活动事件:

User.first.active_events

1
如果您尚未使用Rails 4,您仍然可以遵循这个出色的示例,但是您可能需要使用不推荐使用的方法 :conditions => {active: true}。为我工作,谢谢!
monozok

12

即使您的u.events没有显式调用user_events表,由于必要的联接,该表仍隐式包含在SQL中。因此,您仍然可以在查找条件中使用该表:

u.events.find(:all, :conditions => ["user_events.active = ?", true])

当然,如果你计划,以确保这样的查找了很多话,给它一个独立的关联作为米兰Novota建议,但没有要求为你这样做的


我喜欢在模型中的一个位置定义所有范围,即使只使用一次也是如此。它使我的控制器更薄,整体设计更一致。
米兰诺沃塔09年

8

好吧,更多的责任正在承担 User模型比实际需要的多,并且没有充分的理由这样做。

我们首先可以在EventUser模型中定义范围,因为它实际上属于哪个范围,例如:

class EventUser < ActiveRecord::Base
  belongs_to :event
  belongs_to :user

  scope :active,   -> { where(active: true)  }
  scope :inactive, -> { where(active: false) } 
end

现在,用户可以同时具有两种事件:活动事件和非活动事件,因此我们可以User按以下方式在模型中定义关系:

class User < ActiveRecord::Base
  has_many :active_event_users,   -> { active },   class_name: "EventUser"
  has_many :inactive_event_users, -> { inactive }, class_name: "EventUser"

  has_many :inactive_events, through: :inactive_event_user,
                             class_name: "Event",
                             source: :event
  has_many :active_events,   through: :active_event_users,
                             class_name: "Event",
                             source: :event
end

该技术的优点在于,作为活动或不活动事件的功能属于EventUser模型,如果将来需要修改功能,则只能在一个地方进行修改:EventUser模型,并且更改将反映在所有其他型号。


1
您的解决方案让我
感动
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.