views:

1057

answers:

5

Im trying set the single table inheritance model type in a form. So i have a select menu for attribute :type and the values are the names of the STI subclasses. The problem is the error log keeps printing:

WARNING: Can't mass-assign these protected attributes: type

So i added "attr_accessible :type" to the model:

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

Doesn't change anything, the ContentItem still has :type=nil after .update_attributes() is called in the controller. Any idea how to mass update the :type from a form?

A: 

"type" sometimes causes troubles... I usually use "kind" instead.

See also: http://wiki.rubyonrails.org/rails/pages/ReservedWords

semanticart
+1  A: 

Duplex at railsforum.com found a workaround:

use a virtual attribute in the forms and in the model instead of type dirtectly:

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

Worked like a charm.

Lee
+1  A: 

You should use the proper constructor based on the subclass you want to create, instead of calling the superclass constructor and assigning type manually. Let ActiveRecord do this for you:

# in controller
def create
   # assuming your select has a name of 'content_item_type'
   params[:content_item_type].constantize.new(params[:content_item])
end

This gives you the benefits of defining different behavior in your subclasses initialize() method or callbacks. If you don't need these sorts of benefits or are planning to change the class of an object frequently, you may want to reconsider using inheritance and just stick with an attribute.

ry
A: 

Hello, I got a similar problem. And I don't understand the workaround with the virtuell attribute. I have two models: rohstoffhauptgruppen and rohstoffuntergruppen in a has_many , belongs_to relation. I have a virtuell attribut called :"untergruppen_zeigen" in my model "rohstoffuntergruppen" rohstoffuntergruppen.rb:

> def untergruppen_zeigen=(untergruppen_zeigen) untergruppen_zeigen.each do |zeigen| if attributes[:id].blank? rohstoffuntergruppen.build(zeigen) else rohstoffuntergruppe = rohstoffuntergruppe.detect { |t| t.id == attributes[:id].to_i } rohstoffuntergruppe.attributes = attributes end end end <<

and a attr_accessible

> attr_accessible :id, :rohstoffhauptgruppenname, :untergruppen_zeigen <<

in my rohstoffhauptgruppen_controller I have

> def create @rohstoffhauptgruppe = Rohstoffhauptgruppe.new(params[:rohstoffhauptgruppe]) @rohstoffuntergruppe = @rohstoffhauptgruppe.rohstoffuntergruppen.build(params[:untergruppen_zeigen])

if @rohstoffhauptgruppe.save
 flash[:notice] = "Hauptgruppe wurde gespeichert."
 redirect_to rohstoffhauptgruppen_path
else
 flash[:error] = "Hauptgruppe wurde nicht gespeichtert."
 render :action => :new
end

end def update @rohstoffhauptgruppe = Rohstoffhauptgruppe.find(params[:id]) respond_to do |format| if @rohstoffhauptgruppe.update_attributes(params[:rohstoffhauptgruppe]) flash[:notice] = "Hauptgruppe wurde gespeichert." format.html {redirect_to rohstoffhauptgruppen_path} else flash[:error] = "Hauptgruppe wurde nicht gespeichtert." format.html {redirect_to rohstoffhauptgruppen_path} end end <<

The create action works fine, only the update action raise this:

the params_array looks like this: Parameters: {"commit"=>"Speichern", "authenticity_token"=>"p8Q0O0m4lFxArr0rTbo 3P8s1LJ65t6fCiJn1OMtMd2g=", "id"=>"1", "rohstoffhauptgruppe"=>{"rohstoffhauptgru ppenname"=>"Backwaren", "untergruppen_zeigen"=>[{"rohstoffuntergruppenname"=>"Br ot", "id"=>"1"}, {"rohstoffuntergruppenname"=>"Br├Âtchen", "id"=>"2"}, {"rohstof funtergruppenname"=>"Spezialbrot", "id"=>"3"}, {"rohstoffuntergruppenname"=>"Kuc hen", "id"=>"4"}, {"rohstoffuntergruppenname"=>"Keks", "id"=>"5"}, {"rohstoffunt ergruppenname"=>"Sonstige", "id"=>"6"}]}} ←[4;36;1mRohstoffhauptgruppe Columns (20.0ms)←[0m ←[0;1mSHOW FIELDS FROM ro hstoffhauptgruppen←[0m ←[4;35;1mRohstoffhauptgruppe Load (10.0ms)←[0m ←[0mSELECT * FROM rohstoffha uptgruppen WHERE (rohstoffhauptgruppen.id = 1) ←[0m ←[4;36;1mRohstoffuntergruppe Columns (10.0ms)←[0m ←[0;1mSHOW FIELDS FROM ro hstoffuntergruppen←[0m WARNING: Can't mass-assign these protected attributes: id WARNING: Can't mass-assign these protected attributes: id WARNING: Can't mass-assign these protected attributes: id WARNING: Can't mass-assign these protected attributes: id WARNING: Can't mass-assign these protected attributes: id WARNING: Can't mass-assign these protected attributes: id

If i use only

> attr_accessible :id, :rohstoffhauptgruppenname << the error raised is: WARNING: Can't mass-assign these protected attributes: untergruppen_zeigen

if a assign untergruppen_zeigen as well to attr_accessible

above error message show up.

I have no clou how to resolve that problem. help is welcome!

Thanks TimE

TimE
A: 

I followed http://coderrr.wordpress.com/2008/04/22/building-the-right-class-with-sti-in-rails/ for solving the same problem I had. I'm fairly new to Rails world so am not so sure if this approach is good or bad, but it works very well. I've copied the solution below.

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"}>
Sanghyun Park