我尝试在表单中设置单表继承模型类型。所以我有一个属性选择菜单:类型,值是STI子类的名称。问题是错误日志不断打印:
警告:无法批量分配这些受保护的属性:键入
所以我在模型中添加了“attr_accessible:type”:
class ContentItem < ActiveRecord::Base
# needed so we can set/update :type in mass
attr_accessible :position, :description, :type, :url, :youtube_id, :start_time, :end_time
validates_presence_of :position
belongs_to :chapter
has_many :user_content_items
end
不改变任何内容,ContentItem仍然具有:在控制器中调用.update_attributes()之后的type = nil。知道如何从表单中大量更新:type?
答案 0 :(得分:20)
我们可以覆盖attributes_protected_by_default
class Example < ActiveRecord::Base
def self.attributes_protected_by_default
# default is ["id","type"]
["id"]
end
end
e = Example.new(:type=>"my_type")
答案 1 :(得分:9)
您应该使用基于您要创建的子类的正确构造函数,而不是调用超类构造函数并手动分配类型。让ActiveRecord为您完成此任务:
# in controller
def create
# assuming your select has a name of 'content_item_type'
params[:content_item_type].constantize.new(params[:content_item])
end
这为您提供了在子类initialize()方法或回调中定义不同行为的好处。如果您不需要这些类型的好处或者计划经常更改对象的类,您可能需要重新考虑使用继承并坚持使用属性。
答案 2 :(得分:6)
railsforum.com上的Duplex发现了一种解决方法:
在表单中使用虚拟属性 并在模型而不是类型 dirtectly:
def type_helper
self.type
end
def type_helper=(type)
self.type = type
end
像魅力一样工作。
答案 3 :(得分:4)
“type”有时会引起麻烦......我通常会使用“kind”。
答案 4 :(得分:1)
我跟着http://coderrr.wordpress.com/2008/04/22/building-the-right-class-with-sti-in-rails/解决了我遇到的同样问题。我是Rails世界的新手,所以不太确定这种方法是好还是坏,但它的效果非常好。我复制了下面的代码。
class GenericClass < ActiveRecord::Base
class << self
def new_with_cast(*a, &b)
if (h = a.first).is_a? Hash and (type = h[:type] || h['type']) and (klass = type.constantize) != self
raise "wtF hax!!" unless klass < self # klass should be a descendant of us
return klass.new(*a, &b)
end
new_without_cast(*a, &b)
end
alias_method_chain :new, :cast
end
class X < GenericClass; end
GenericClass.new(:type => 'X') # => #<X:0xb79e89d4 @attrs={:type=>"X"}>