2009-10-21 67 views
4

我试着在表单中设置单表继承模型类型。所以我有一个选择菜单的属性:类型和值是STI子类的名称。问题是错误日志保持印刷:Rails attr_accessible不适用于:type?

警告:不能批量分配这些保护属性:类型

,所以我说“attr_accessible:类型”的模式:

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仍然有:type = nil,在控制器中调用.update_attributes()之后。任何想法如何大规模更新表单中的:类型?

回答

6

双工在railsforum.com发现一种解决方法:

在形式 和在模型中的代替 类型dirtectly使用虚拟属性

def type_helper 
    self.type 
end 
def type_helper=(type) 
    self.type = type 
end 

工作就像一个魅力。

+0

真棒!正是我需要的。 – jspooner 2011-03-04 19:53:31

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()方法或回调中定义不同行为的好处。如果您不需要这些好处或计划频繁更改对象的类别,则可能需要重新考虑使用继承,并仅使用一个属性。

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"}> 
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") 
相关问题