Rails: has_many通过多态关联——这有用吗?

时间:2022-09-23 08:00:10

A Person can have many Events and each Event can have one polymorphic Eventable record. How do I specify the relationship between the Person and the Eventable record?

一个人可以有许多事件,每个事件可以有一个多态的可证明的记录。如何指定Person与可事件记录之间的关系?

Here are the models I have:

以下是我的模型:

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

The main question concerns the Person class:

主要问题涉及到Person类:

class Person < ActiveRecord::Base
  has_many :events
  has_many :eventables, :through => :events  # is this correct???
end

Do I say has_many :eventables, :through => :events like I did above?

我是说has_many:eventables:through =>:像上面那样的事件?

Or do I have to spell them all out like so:

或者我必须像这样把它们拼出来:

has_many :meals, :through => :events
has_many :workouts, :through => :events

If you see an easier way to accomplish what I'm after, I'm all ears! :-)

如果你能找到一种更简单的方法来实现我的目标,我将洗耳恭听!:-)

2 个解决方案

#1


68  

You have to do:

你要做的:

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

This will enable you to do this:

这将使你能够做到:

p = Person.find(1)

# get a person's meals
p.meals.each do |m|
  puts m
end

# get a person's 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


1  

Another option of this is to use a Single Table Inheritance (STI) or Multi Table Inheritance (MTI) pattern, but that requires some ActiveRecord/DB Table rework, but this may help others still finding this who are designing it for the first time.

另一种选择是使用单表继承(STI)或多表继承(MTI)模式,但这需要一些ActiveRecord/DB表的重写,但这可能会帮助那些第一次设计它的人发现这一点。

Here is the STI method in Rails 3+: Your Eventable concept becomes a class and needs a type column (which rails automatically populates for you).

以下是Rails 3+中的STI方法:您的Eventable将成为一个类,需要一个类型列(Rails会自动为您填充)。

class Eventable < ActiveRecord::Base
  has_one :event
end

Then, your other two classes inherit from Eventable instead of AR::Base

然后,您的另外两个类继承自Eventable,而不是AR: Base

class Meal < Eventable
end

class Workout < Eventable
end

And your event object is basically the same, just not polymorphic:

你的事件对象基本上是一样的,只是不多态性:

class Event < ActiveRecord::Base
  belongs_to :person
  belongs_to :eventable
end

This may make some of your other layers more confusing, if you've never seen this before and you're not careful. For example, a single Meal object can be accessed at /meals/1 and /eventable/1 if you make both endpoints available in the routes, and you need to be aware of the class you're using when you pull an inherited object (hint: the becomes method may be very useful if you need to override the default rails behavior)

如果您以前从未见过这种情况,并且不小心的话,这可能会使您的其他一些层更加混乱。例如,一个餐对象可以访问/餐/ 1和/ eventable / 1如果你让两个端点中可用的路线,和你需要注意类的使用当你把一个继承的对象(提示:成为方法可能非常有用,如果你需要覆盖默认的rails行为)

But this is a much cleaner deliniation of responsibilities as apps scale, in my experience. Just a pattern to consider.

但根据我的经验,随着应用程序的规模扩大,这是一种更清晰的职责分工。这只是一个需要考虑的模式。

#1


68  

You have to do:

你要做的:

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

This will enable you to do this:

这将使你能够做到:

p = Person.find(1)

# get a person's meals
p.meals.each do |m|
  puts m
end

# get a person's 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


1  

Another option of this is to use a Single Table Inheritance (STI) or Multi Table Inheritance (MTI) pattern, but that requires some ActiveRecord/DB Table rework, but this may help others still finding this who are designing it for the first time.

另一种选择是使用单表继承(STI)或多表继承(MTI)模式,但这需要一些ActiveRecord/DB表的重写,但这可能会帮助那些第一次设计它的人发现这一点。

Here is the STI method in Rails 3+: Your Eventable concept becomes a class and needs a type column (which rails automatically populates for you).

以下是Rails 3+中的STI方法:您的Eventable将成为一个类,需要一个类型列(Rails会自动为您填充)。

class Eventable < ActiveRecord::Base
  has_one :event
end

Then, your other two classes inherit from Eventable instead of AR::Base

然后,您的另外两个类继承自Eventable,而不是AR: Base

class Meal < Eventable
end

class Workout < Eventable
end

And your event object is basically the same, just not polymorphic:

你的事件对象基本上是一样的,只是不多态性:

class Event < ActiveRecord::Base
  belongs_to :person
  belongs_to :eventable
end

This may make some of your other layers more confusing, if you've never seen this before and you're not careful. For example, a single Meal object can be accessed at /meals/1 and /eventable/1 if you make both endpoints available in the routes, and you need to be aware of the class you're using when you pull an inherited object (hint: the becomes method may be very useful if you need to override the default rails behavior)

如果您以前从未见过这种情况,并且不小心的话,这可能会使您的其他一些层更加混乱。例如,一个餐对象可以访问/餐/ 1和/ eventable / 1如果你让两个端点中可用的路线,和你需要注意类的使用当你把一个继承的对象(提示:成为方法可能非常有用,如果你需要覆盖默认的rails行为)

But this is a much cleaner deliniation of responsibilities as apps scale, in my experience. Just a pattern to consider.

但根据我的经验,随着应用程序的规模扩大,这是一种更清晰的职责分工。这只是一个需要考虑的模式。