2009-10-21 72 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")