STI有很多种类型

时间:2013-11-16 17:45:09

标签: ruby-on-rails ruby-on-rails-4 sti

我有一个带有STI型号的rails 4应用程序:

# models/person.rb
def Person < ActiveRecord::Base

end

# models/director.rb
def Director < Person

end

# models/actor.rb
def Director < Person

end

但是因为一个人可以同时扮演演员和导演,所以我希望STI有很多类型:

person = Person.first
person.type = "Director, Actor"
person.save

Actor.first.id => 1

Director.first.id => 1

轨道或宝石中是否有机制来实现这个目标?

3 个答案:

答案 0 :(得分:0)

Rails不支持这一点,我不知道任何支持这个的宝石(即类型列中的多个子类名称)。

https://github.com/mhuggins/multiple_table_inheritance有一个gem,它为子类使用单独的表,你总是可以使用mixins作为继承的替代方法。

答案 1 :(得分:0)

我相信更多Rails惯用的做法类似的方法是通过scopes,这将允许你这样做:

person = Person.first
person.position = 'Director, Actor'
person.save

person.directors.first.id => 1
person.actors.first.id => 1

您只需要在Person类中定义一对范围:

scope :actors, -> { where('position like ?', '%Actor%') }
scope :directors, -> { where('position like ?', '%Director%') }

你会失去用person.is_a?做这个的能力,但是Ruby并没有真正做多重继承,以至于无论如何都允许#is_a?在传递兄弟类时返回true。您还可以通过简单的测试方法获得有效的类似功能:

def is_actor?
    self.position =~ /Actor/
end
def is_director?
    self.position =~ /Director/
end
编辑:我没有做过很多Rails 4,所以我的scope语法可能不正确,我只是看了一下文档。但原则应该是合理的。

答案 2 :(得分:0)

感谢上面的所有回答者!

我找到了最适合我的解决方案: 我创建了hmt association Person-ProfessionsPerson-Profession并为Person类(导演和演员)留下了后代。

# models/profession.rb
Profession < ActiveRecord::Base
  has_many :professions_people, dependent: :destroy
  has_many :people, through: :professions_people
end

# models/person.rb
def Person < ActiveRecord::Base
  has_many :professions_people, dependent: :destroy
  has_many :professions, through: :professions_people
end

# models/director.rb
def Director < Person
  include PeopleFromProfession
end

# models/actor.rb
def Actor < Person
  include PeopleFromProfession
end

我已经为2个职业种下了“class_type”栏目(在应用程序的工作中不应该改变)“演员”和“导演” 我还添加了关注PeopleFromProfession以分享一些代码:

# models/concerns/actor.rb
module PeopleFromProfession
  extend ActiveSupport::Concern

  included do
    default_scope { includes(:professions).where(professions: {class_type: self.name}) }

    after_create :create_join_table_record
  end

  module ClassMethods
    def model_name
      Person.model_name
    end
  end

private
  def create_join_table_record
    self.professions << Profession.where(class_type: self.class.name).first
  end

end

default_scope仅适用于具有特定职业的人,create_join_table_record回调是用于创建错过的联接表记录的monkey-patch。

类方法model_name被覆盖用于此目的,Best practices to handle routes for STI subclasses in rails

如果您在这种方法中遇到一些问题,请告诉我。