Новичок как в Ruby, так и в Rails, но к настоящему времени я получил книжное образование (что, по-видимому, ничего не значит, ха-ха).
У меня есть две модели: событие и пользователь, объединившиеся через таблицу 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
Это как бы противоречит «рельсовому пути». Может ли кто-нибудь просветить меня, это уже давно меня беспокоит сегодня вечером (сегодня утром)?





Как насчет того, чтобы добавить что-то подобное в вашу модель User?
has_many :active_events, :through => :event_users,
:class_name => "Event",
:source => :event,
:conditions => ['event_users.active = ?',true]
После этого вы сможете получать активные события для пользователя, просто позвонив:
User.first.active_events
Я пометил вас, но если я не получу лучшего ответа к утру, то он ваш.
См. Мой ответ ниже, чтобы узнать о более актуальном методе.
Спасибо, Rails 4 способ: -> { where event_users: { active: true } }
Но нет ли шанса, что условие будет определяться прицелом event_users? Нравится scope :active, where(:active => true)? (с использованием Rails 3.2)
@IvanBlack, не могли бы вы объяснить, как это вписывается в has_many through:? Не могли бы вы включить полный эквивалент Rails 4?
@DamienRoche, ->{where...} вместо :conditions. полная версия
Даже если ваш u.events не является явно, вызывающим таблицу user_events, эта таблица по-прежнему включена в SQL неявно из-за необходимых объединений. Итак, вы все еще можете использовать эту таблицу в своих условиях поиска:
u.events.find(:all, :conditions => ["user_events.active = ?", true])
Конечно, если вы планируете часто выполнять этот поиск, тогда обязательно дайте ему отдельную ассоциацию, как предлагает Милан Новота, но для вас нет требование, чтобы сделать это таким образом
Мне нравится определять все области видимости в одном месте - в модели, даже если я использую их только один раз. Благодаря этому мои контроллеры остаются тонкими, а общий дизайн - более согласованным.
У Milan 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
Если вы еще не используете Rails 4, вы все равно можете следовать этому отличному примеру, но вам может потребоваться использовать устаревший :conditions => {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, и изменения будут отражено во всех остальных моделях.
ваше решение сделало мой день :)
Вы также можете создать именованную область, используя предложение Гарета.