Can nested attributes be used in combination with inheritance?
Asked Answered
N

4

7

I have the following classes:

  • Project
  • Person
  • Person > Developer
  • Person > Manager

In the Project model I have added the following statements:

has_and_belongs_to_many :people
accepts_nested_attributes_for :people

And of course the appropriate statements in the class Person. How can I add a Developer to a Project through the nested_attributes method? The following does not work:

@p.people_attributes = [{:name => "Epic Beard Man", :type => "Developer"}]
@p.people
=> [#<Person id: nil, name: "Epic Beard Man", type: nil>]

As you can see the type attributes is set to nil instead of "Developer".

Needlecraft answered 31/3, 2010 at 14:50 Comment(0)
T
5

I encountered a similar problem few days ago. The inheritance column(i.e. type) in a STI model is a protected attribute. Do the following to override the default protection in your Person class.

Rails 2.3

class Person < ActiveRecord::Base

private
  def attributes_protected_by_default
    super - [self.class.inheritance_column]
  end
end

Rails 3

Refer to the solution suggested by @tokland.

Caveat:

You are overriding the system protected attribute.

Reference:

SO Question on the topic

Tameika answered 31/3, 2010 at 18:58 Comment(4)
note: this works for Rails 2.3, see my answer below for Rails 3Curvaceous
@tokland, I hadn't visited this question for sometime. I have updated my answer with a Rails 3 solution.Tameika
great. I'd write a simple def self.attributes_protected_by_default, but whatever.Curvaceous
@tokland, you are right over-riding the class method is easier. When I was testing that approach, as I had a funky object/class state in my irb session and the super call didn't work.Tameika
C
7

Solution for Rails3: attributes_protected_by_default in now a class-method:

class Person < ActiveRecord::Base

  private

  def self.attributes_protected_by_default
    super - [inheritance_column]
  end
end
Curvaceous answered 4/7, 2012 at 9:0 Comment(1)
Note: Make sure you put this near the top of the class, before any calls to attr_protected. The first call to attr_protected will cause it to call whatever version of attributes_protected_by_default is currently defined, which will be the default version from AR unless you've overridden it by that point...Nordine
T
5

I encountered a similar problem few days ago. The inheritance column(i.e. type) in a STI model is a protected attribute. Do the following to override the default protection in your Person class.

Rails 2.3

class Person < ActiveRecord::Base

private
  def attributes_protected_by_default
    super - [self.class.inheritance_column]
  end
end

Rails 3

Refer to the solution suggested by @tokland.

Caveat:

You are overriding the system protected attribute.

Reference:

SO Question on the topic

Tameika answered 31/3, 2010 at 18:58 Comment(4)
note: this works for Rails 2.3, see my answer below for Rails 3Curvaceous
@tokland, I hadn't visited this question for sometime. I have updated my answer with a Rails 3 solution.Tameika
great. I'd write a simple def self.attributes_protected_by_default, but whatever.Curvaceous
@tokland, you are right over-riding the class method is easier. When I was testing that approach, as I had a funky object/class state in my irb session and the super call didn't work.Tameika
V
4

Patches above did not work for me, but this did (Rails3):

class ActiveRecord::Reflection::AssociationReflection
  def build_association(*options)
    if options.first.is_a?(Hash) and options.first[:type].presence
      options.first[:type].to_s.constantize.new(*options)
    else
      klass.new(*options)
    end
  end
end

Foo.bars.build(:type=>'Baz').class == Baz

Vivyanne answered 3/6, 2010 at 20:22 Comment(1)
This solution works for associations, but not for stand along STI object construction. I have updated my answer with a generic Rails 3 solution.Tameika
L
0

For those of us using Mongoid, you will need to make the _type field accessible:

class Person
  include Mongoid::Document
  attr_accessible :_type
end
Lyndy answered 29/6, 2013 at 3:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.