Rails STI和Active Record发现

时间:2015-02-27 23:53:21

标签: ruby-on-rails activerecord sti

我有一组这样的类:

class Owner
  has_one :animal
end

class Animal < AR::Base
  TYPES = ['Lion','Cat']
  # has a type field

  belongs_to :owner
end

class Lion < Animal
  def speak
    'roar'
  end
end

class Cat < Animal
  def speak
    'meow'
  end
end

所以我与所有者和动物有关联,每只动物都有一种类型。这一切都有效,直到我这样打电话:

owner.animal.speak

返回类型为&#39; Animal&#39;然后尝试调用该方法&#39;说&#39;这是父母未定义的。如何让Rails返回适当的子类而不是父类?

2 个答案:

答案 0 :(得分:0)

ActiveRecord和Arel不容易像这样处理子类。你最好的选择是让Animal类在内部跟踪它自己的类型。所以:

class Animal < AR::Base
  TYPES = ['Lion','Cat']

  belongs_to :owner

  def speak
     case self.type
     when "Lion"
        "roar"
     when "Cat"
        "meow"
     end
  end
end

答案 1 :(得分:0)

我刚刚启动了一个新的Rails项目并使用了您的示例,它按预期工作。在type列中,您确定类型为"Cat"还是"Lion",而不是小写或下标版本?

我建议将has_one :cathas_one :lion添加到Owner。然后,要实例化它们,您可以调用Rails'owner.build_cat,这是构建新实例的has_one方式。 (在has_many owner.cats.build。{/ p>

这样,您确定type已正确完成。

这就是我得到的。

> owner.animal
=> nil
> owner.cat
=> nil
> owner.build_cat
=> #<Cat id: nil, owner_id: 1, type: "Cat" ...
> owner.cat.save
=> true
> owner.cat 
=> #<Cat id: 1, owner_id: 1, type: "Cat" ...
> owner.animal
=> nil # Whaaaa?

(所有者实例有一个关联缓存,因为我们已经调用了动物,它是零,它已被缓存为零)

> owner.clear_association_cache
=> {}
> owner.animal
=> #<Cat id: 1, owner_id: 1, type: "Cat" ...
> owner.animal.speak
=> 'meow'
> owner.animal.class
=> Cat
> owner.animal.class.to_s == owner.animal.type
=> true

希望有所帮助!