Monday, November 2, 2009

Single Table Inheritance and accepts_nested_attributes_for

I recently upgraded our web app at Episodic from Rails 2.0 to Rails 2.3.4. While this resulted in many issues we had to resolve that I will try to write about in later post, it does allow me to take advantage of some of the Rails 2.3.x features like accepts_nested_attributes_for. If you aren’t familiar with accepts_nested_attributes_for then check out Ryan’s post.

So I went ahead and tried to make use of this in my object model. Here are the important parts of the model.


class Episode < ActiveRecord::Base
has_many :field_values, :dependent => :destroy
accepts_nested_attributes_for :field_values, :allow_destroy => true
end

class FieldValue < ActiveRecord::Base
:belongs_to :episode
end

class TextFieldValue < FieldValue
end

class NumberFieldValue < FieldValue
end


Since I am using Single Table Inheritance I end up with a field_values table that has a column named “type” which contain either a value of “TextFieldValue” or “NumberFieldValue”.

Also, because I enabled accepts_nested_attributes_for on my Episode class I should be able to do something like:


episode.field_values_attributes =
[{:value => “foo”, :type => “TextFieldValue”}]


However, when I look in the DB I see that there is a new row but the “type” column is NULL even though I set it to “TextFieldValue”. When I look in the logs I see: "Can't mass-assign these protected attributes: type".

Luckily, there is a way around this. I added a setter called "value_type" to my FieldValue class.


def value_type= value_type
self.type = value_type
end


Now, when I can safely use field_values_attributes and set a type.


episode.field_values_attributes =
[{:value => “foo”, :value_type => “TextFieldValue”}]

3 comments:

  1. I shouldn't be surprizing so hard at that.

    ReplyDelete
  2. I just hit this. Thanks for the post!!

    ReplyDelete
  3. This worked for me (Rails 3.1):

    class MyModel
    ...

    private

    def self.attributes_protected_by_default
    [self.primary_key]
    end
    end

    ReplyDelete