This project is archived and is in readonly mode.

#4944 ✓resolved
ryanza

accepts_nested_attributes_for and attr_accessor

Reported by ryanza | June 23rd, 2010 @ 11:18 AM

I have a model that has one attr_accessor, When that gets updated through a nested_attributes form, it does not pick up the change to the attr_accessor, and does not run any of the callbacks.

I have to edit another attribute that is actually part of the model, for the callbacks to occur.

For example:

class Resource < ActiveRecord::Base
  # ...
  has_many :items
  
  accepts_nested_attributes_for :items, :allow_destroy => true
end
class Item < ActiveRecord::Base
  belongs_to :resource
  
  before_validation :clear_if_available
  
  attr_accessor :unavailable

  def clear_if_available
    if @unavailable == 'yes'
      # ...
    end
  end
end

# Resource.rb

=> r = Resource.new
#<Resource id: 1, quantity: 1, maximum_capacity: 10, name: "Lolcat", supplier_id: 1, created_at: "2010-06-22 15:09:57", updated_at: "2010-06-22 15:09:57", category: "other">

=> Item.first
#<Item id: 1, name: "Lolcat Item", resource_id: 1, out_from: nil, out_to: nil, created_at: "2010-06-22 15:09:57", updated_at: "2010-06-23 09:30:36", mobile_number: nil, email: nil, link_id: 1, points: 0, unavailable: false>

=> r.update_attributes(:items_attributes => { :id => 1, :name => "Lolcat changed, :unavailable => 'yes' })
true # This does the callbacks and does my update

=> r.update_attributes(:items_attributes => { :id => 1, :unavailable => 'yes' })
true # This ignores my update to unavailable and does nothing

This used to work in Rails 2.3.5 now not working in Rails 2.3.8?

Comments and changes to this ticket

  • Neeraj Singh

    Neeraj Singh June 26th, 2010 @ 05:22 AM

    If I understand you correctly then attribute "unavailable" is not a column on Item table. In that case r.update_attributes(:items_attributes => { :id => 1, :unavailable => 'yes' }) has nothing to do.

    Am I missing something here?

  • Brad Crawford

    Brad Crawford July 7th, 2010 @ 08:21 AM

    • Importance changed from “” to “Low”

    You are missing the fact that in 2.3.5, this worked. :)

  • Brad Crawford

    Brad Crawford July 7th, 2010 @ 08:31 AM

    Ryanza, I am having the same issue but after creating a blank 2.3.8 project it no longer occurs. I am thinking that it is probably a gem/plugin issue. I will try and track it down and get back to you.

  • ryanza

    ryanza July 7th, 2010 @ 08:45 AM

    Neeraj Singh,

    Its not firing off any of the hooks in the Item model, where it used to in 2.3.5 (on update_attributes and saves). I do understand that the way it is now (2.3.8) should actually be the correct response (do nothing as nothing is being modified), but it is breaking my old functionality, is there a workaround? :)

    Brad Crawford,

    I saw alot of code was changed/refactored in the accepts_nested_attributes_for method, I haven't had enough time to actually dig through it, I'll pull out a diff tonight and see if i can spot anything.

    Ill try test this in a new Rails 2.3.8 project; makes no sense that it would work?

    Thanks :)

  • Neeraj Singh

    Neeraj Singh July 7th, 2010 @ 11:18 AM

    @ryanza can you show us full implementation of method clear_if_available?

  • ryanza

    ryanza July 7th, 2010 @ 11:21 AM

    Sure:

      def clear_dates_if_available
        if self.unavailable == false || !self.book_out_expired?
          self.out_from, self.out_to = nil, nil
        end
      end
    
  • Brad Crawford

    Brad Crawford July 7th, 2010 @ 11:42 AM

    Ok I might have spoken too soon, it doesn't actually work for a blank 2.3.8 app. I haven't looked into the code but as you said, none of the callbacks are being executed on the nested model when update_attributes is called on the parent after setting an attr_accessor attribute on the nested model. It is not dirty I guess. Any ideas for a workaround? 2.3.8 is a no go until I find one.

  • ryanza

    ryanza July 7th, 2010 @ 12:03 PM

    I thought it had to do with dirty attributes, so i tried to fake it by writing my own dirty methods for the attr_accessor, and had no luck there either.. Hopefully some sort of solution is figured out for this, as its not REALLY a bug..

  • ryanza

    ryanza July 7th, 2010 @ 12:37 PM

    Maybe "accepts_nested_attributes_for :items, :ignore_dirty => true" or something similar?

  • Neeraj Singh

    Neeraj Singh July 7th, 2010 @ 12:48 PM

    Is it true that in the example that is mentioned at the very top you are changing :unavailable from false to 'yes'; from boolean to string?

  • ryanza

    ryanza July 7th, 2010 @ 12:52 PM

    Neeraj, Yes I changed it from 'yes'/'no' to true/false

  • Neeraj Singh

    Neeraj Singh July 16th, 2010 @ 04:19 AM

    Not able to reproduce the problem in rails edge. I am getting the callbacks.

    Here is what I did

    ActiveRecord::Schema.define(:version => 20100716030954) do
      create_table "questions", :force => true do |t|
        t.text    "name"
        t.integer "survey_id"
      end
      create_table "surveys", :force => true do |t|
        t.string "name"
      end
    end
    class Question < ActiveRecord::Base
      belongs_to :survey
    
      before_validation :doit
    
      def doit
        puts 'done' * 20
      end
    
      attr_accessor :sex
    end
    class Survey < ActiveRecord::Base
    
      has_many :questions, :dependent => :destroy
    
      accepts_nested_attributes_for :questions, :allow_destroy => true
    
      validates_presence_of :name
    
      def self.lab
        survey = Survey.create!(:name => "Current Trend in Programming")
        question = survey.questions.create!(:name => "Java", :sex => 'a')
        survey.update_attributes({:name => 'hello', :questions_attributes => [{:id => question.id, :sex => 'b' }]})
      end
    end
    
  • Andreas Mayer

    Andreas Mayer July 25th, 2010 @ 04:09 PM

    Can confirm this for Rails 3 Beta 4

  • Andreas Mayer

    Andreas Mayer July 26th, 2010 @ 05:17 PM

    Solved it using

    class TutorialStep < ActiveRecord::Base
      belongs_to :tutorial
      attr_accessor :download_remove
    
    ...
    
      def download_remove=(remove)
        self.download = nil if remove == '1'
      end
    end
    

    which doesn't require a callback and solves the dirty issue because setting download_remove changes download and makes the record dirty (because the download shall be removed)

  • Neeraj Singh

    Neeraj Singh July 26th, 2010 @ 05:28 PM

    • State changed from “new” to “resolved”

    awesome. closing it.

  • matthew.willhite (at gmail)

    matthew.willhite (at gmail) October 29th, 2010 @ 07:02 PM

    I am having the same issue as the original post.

    # page.rb
    class Page < ActiveRecord::Base
      has_many :page_elements, :dependent => :destroy
      accepts_nested_attributes_for :page_elements, :allow_destroy => true
    end
    
    # page_element.rb
    class PageElement < ActiveRecord::Base
      belongs_to :page
      belongs_to :media, :polymorphic => true
        
      attr_accessor :media_attributes
    
      after_update :update_media  
        
      def update_media
        self.media.update_attributes(media_attributes)
      end
      
    end
    
    Page.find(2).update_attributes(:page_elements_attributes => [{ :id=>3, :scale => 2, :media_attributes => { :source => "iphone"} }])
    

    (This will trigger the page element callbacks because an actual (non-virtual) attribute is being modified)

    Page.find(2).update_attributes(:page_elements_attributes => [{ :id=>3, :media_attributes => { :source => "test15"} }])
    

    (This will not trigger the page element callbacks because only the virtual attribute is being modified)

    Is this the intended behavior? If not, I think this ticket needs to be reopened.

    Using Rails 3.0.1, Ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages