Rails has_many: через поиск по дополнительным атрибутам в модели соединения
-
03-07-2019 - |
Вопрос
Я новичок в 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
модель, и изменения отразятся на всех остальных моделях.