Rails has_many: через поиск по дополнительным атрибутам в модели соединения - PullRequest
70 голосов
/ 03 января 2009

Новичок как в 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

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

Ответы [ 4 ]

123 голосов
/ 03 января 2009

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

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

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

User.first.active_events
22 голосов
/ 19 ноября 2013

Милан Новота имеет хорошее решение - но :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
12 голосов
/ 03 января 2009

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

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

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

7 голосов
/ 10 ноября 2016

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

...