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

StackOverflow https://stackoverflow.com/questions/408872

Вопрос

Я новичок в 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

Кажется, это противоречит «рельсовому пути».Может ли кто-нибудь просветить меня, сегодня вечером (сегодня утром) это беспокоило меня уже давно?

Это было полезно?

Решение

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

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

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

User.first.active_events

Другие советы

У Милана Новота есть хорошее решение – но :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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top