Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

problem with combined use of touch and autosave options

#3096

In certain circumstances the combined use of :touch and :autosave options provokes updates in the database of objects that have not changed.

Here is an example:

class User < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy, :autosave => true
end
class Group < ActiveRecord::Base
  has_many :memberships, :dependent => :destroy, :autosave => true
end
class Membership < ActiveRecord::Base
  belongs_to :user, :touch => true
  belongs_to :group, :touch => true
end

Having a user Joe, a group Soccerteam and a membership between Joe and Soccerteam, if
1. I call the user 'memberships' method,
2. then update an attribute of the user (for instance if name)
the group Soccerteam will be updated in the database tough none of its own attributes has changed.

Here a console illustrative sequence:
http://pastie.org/594701

This bug does not occur if I do not call the user 'memberships' method before updating one of its attribute. It does not occur either without the :touch option, or without the :autosave option.

Tested with Rails 2.3.3

Reported by Florent2 · August 25th, 2009 @ 10:53 PM

State: resolved
Milestone: 2.3.6
Assigned to: Eloy Duran Eloy Duran
Importance: none

Activity

  1. Jamie Rumbelow
    Jamie Rumbelow

    This behavior seems correct - calling the user.memberships touches the group (updating one of it's attributes) and then the autosave falls back down through the associations to find that the group has been changed, thus updating it's attribute.

    Nothing wrong here.

    August 26th, 2009 @ 10:47 AM

  2. Florent2
    Florent2

    Jamie I am not sure I understand what you wrote.

    From what I see in my console sequence, calling "user.memberships" does not touch the group, it just loads the membership associated to the user. When I save the user, the membership is not updated in the DB as it has not changed itself. As the membership record does not change in the database, I don't understand why the group is updated in the database.

    Sounds like a bug for me.

    August 26th, 2009 @ 05:14 PM

  3. Florent2
    Florent2

    I've looked in ActiveRecord code and now I think I understand what you mean Jamie.

    :autosave option calls the save method of the loaded association records. In my case the association (membership) has not changed so when its save method is called, the DB record is not changed.

    But calling this save method is enough for the touch mechanism to occur, that's why the group is updated in the database. This is because touch is implemented as a after_save callback.

    I could "fix" my problem by changing the autosave code so that it calls the association save method only if the association itself has changed. In activerecord-2.3.3/lib/active_record, method save_collection_association I changed the last condition

    elsif autosave
      record.save(false)
    end
    
    into
    elsif autosave && record.changed?
      record.save(false)
    end


    I am not familiar with Rails internals and structure, so before going further and submitting a patch with tests, I would like to know if it the right way to fix this issue. Any indication would appreciated :)

    August 26th, 2009 @ 06:18 PM

  4. Kane
    Kane

    only saving the record when it changed seems fine with me and should be done even if it would work with your touch example.
    if somebody needs to save all associated records every time for what ever he might need touch for has_many.

    i thought of a case where people validate the association record with stuff from the parent, say association.name must be present if parent is in state open or something like this. but that wouldnt be affected cause validations would still be done.

    so +1 from me for adding "&& record.changed?"

    August 28th, 2009 @ 04:38 PM

  5. Eloy Duran
    Eloy Duran
    • Assigned user set to Eloy Duran
    • Milestone set to 2.3.6

    December 30th, 2009 @ 07:42 PM

  6. Eloy Duran
    Eloy Duran
    • State changed from new to resolved

    January 8th, 2010 @ 11:21 AM

  7. Florent2