Rails has_many: через поиск по дополнительным атрибутам в модели объединения

Новый для 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 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

Как добавить что-то подобное в свою модель пользователя?

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

После этого вы сможете получать активные события для пользователя, просто позвонив:

User.first.active_events

Ответ 2

Милан 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

Ответ 3

Несмотря на то, что ваши u.events явно не вызывают таблицу user_events, эта таблица по-прежнему включена в SQL неявно из-за необходимых объединений. Таким образом, вы можете использовать эту таблицу в своих условиях поиска:

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

Конечно, если вы планируете делать этот поиск много, то уверены, дайте ему отдельную ассоциацию, как предлагает "Милан Новота", но нет необходимости в том, чтобы вы это делали.

Ответ 4

Ну, больше ответственности ставится в модель 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 модель, и изменения будут отражены во всех других моделях.