Rails: has_many through with polymorphic association - будет ли это работать?
A Person
может иметь много Events
, и каждый Event
может иметь одну полиморфную запись Eventable
. Как указать связь между записью Person
и Eventable
?
Вот модели, которые у меня есть:
class Event < ActiveRecord::Base
belongs_to :person
belongs_to :eventable, :polymorphic => true
end
class Meal < ActiveRecord::Base
has_one :event, :as => eventable
end
class Workout < ActiveRecord::Base
has_one :event, :as => eventable
end
Основной вопрос касается класса Person
:
class Person < ActiveRecord::Base
has_many :events
has_many :eventables, :through => :events # is this correct???
end
Я говорю has_many :eventables, :through => :events
, как я сделал выше?
Или мне нужно написать все так:
has_many :meals, :through => :events
has_many :workouts, :through => :events
Если вы видите более простой способ добиться того, что мне нужно, я все уши!: -)
Ответы
Ответ 1
Вам нужно сделать:
class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
end
Это позволит вам сделать это:
p = Person.find(1)
# get a person meals
p.meals.each do |m|
puts m
end
# get a person workouts
p.workouts.each do |w|
puts w
end
# get all types of events for the person
p.events.each do |e|
puts e.eventable
end
Ответ 2
Другим вариантом этого является использование шаблона наследования одиночного табличного наследования (STI) или Multi Table Inheritance (MTI), но для этого требуется некоторое повторное использование ActiveRecord/DB Table, но это может помочь другим, находящимся в поиске этого, кто его проектирует первый раз.
Вот метод STI в Rails 3+:
Концепция Eventable становится классом и нуждается в столбце type
(который автоматически настраивается для вас).
class Eventable < ActiveRecord::Base
has_one :event
end
Затем ваши другие два класса наследуют от Eventable вместо AR:: Base
class Meal < Eventable
end
class Workout < Eventable
end
И ваш объект события в основном тот же, просто не полиморфный:
class Event < ActiveRecord::Base
belongs_to :person
belongs_to :eventable
end
Это может сделать некоторые из ваших других слоев более запутанными, если вы никогда не видели этого раньше, и вы не будете осторожны. Например, к одному объекту Meal можно получить доступ к /meals/1
и /eventable/1
, если вы сделаете обе конечные точки доступными в маршрутах, и вам нужно знать о классе, который вы используете, когда вы тянете унаследованный объект (подсказка: метод becomes
может быть очень полезен, если вам необходимо переопределить поведение рельсов по умолчанию)
Но в моем опыте это гораздо более чистое разделение ответственности по масштабам приложений. Просто образец для рассмотрения.