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

class FieldValue < ActiveRecord::Base
:belongs_to :episode

class TextFieldValue < FieldValue

class NumberFieldValue < FieldValue

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

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

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