This project is archived and is in readonly mode.

#2712 ✓resolved
Andrew Kaspick

has_one and after_create association bug

Reported by Andrew Kaspick | May 25th, 2009 @ 10:22 AM | in 2.x

Summary of the bug (rails 2.3.2)...

Two simple classes (Account and Contact). The contact is created after the account is created (via after_create). The "name" field is not passed in to 'create' which the contact requires via validates_presence_of, so the contact should not get saved, but it in fact does.

class Account < ActiveRecord::Base
  has_one :contact
  def after_create
    create_contact
  end
end

class Contact < ActiveRecord::Base
  belongs_to :account
  validates_presence_of :name
end

The test...

class AccountTest < ActiveSupport::TestCase
  test "this ain't right" do
    a = Account.create
    p a.contact
    p a.contact.errors
    p a.new_record?
  end
end

produces...

#<Contact id: 996332878, name: nil, account_id: 1, created_at: "2009-05-25 05:40:40", updated_at: "2009-05-25 05:40:40">
#<ActiveRecord::Errors:0xb6e442a4 @base=#<Contact id: 996332878, name: nil, account_id: 1, created_at: "2009-05-25 09:21:38", updated_at: "2009-05-25 09:21:38">, @errors={"name"=>["can't be blank"]}>
false

The contact has errors and should not have been saved.

I tried debugging the associations code, but came up empty.

Comments and changes to this ticket

  • Daniel Guettler

    Daniel Guettler May 25th, 2009 @ 03:42 PM

    I can confirm this bug in the current gem version. However it has already been fixed by this commit: http://github.com/rails/rails/commit/a70c78177a564c2f2cd09846a5e7ab...

  • CancelProfileIsBroken

    CancelProfileIsBroken May 25th, 2009 @ 03:56 PM

    • State changed from “new” to “resolved”
  • Andrew Kaspick

    Andrew Kaspick May 25th, 2009 @ 07:34 PM

    Is "rake rails:freeze:edge" supposed to pull a copy of rails with this fix?

    I grabbed edge, but am still experiencing the issue.

  • Andrew Kaspick

    Andrew Kaspick May 25th, 2009 @ 08:44 PM

    Looks like the rake command has the "fix", but the bug still exists because :autosave is true even if I set it to false with...

    has_one :contact, :autosave => false
    
  • Andrew Kaspick

    Andrew Kaspick May 25th, 2009 @ 08:49 PM

    My error... the case outlined in this ticket is resolved.

    Reading the code/docs here I see...

    Note that the :autosave option is automatically enabled on every

    # association that accepts_nested_attributes_for is used for.
    

    This is the situation in my production code; I'm using ccepts_nested_attributes_for.

    Should :autosave => false on the association not override the automatica enabling of the :autosave option in this case?

  • Andrew Kaspick

    Andrew Kaspick May 25th, 2009 @ 08:59 PM

    • Tag changed from 2-3-stable, after_create, associations, bug, has_one to 2-3-stable, accepts_nested_attributes_for, after_create, associations, bug, has_one

    Sorry for all the messages. I've removed the use of accepts_nested_attributes_for in my code and now things work.

    The issue is shown with this modification to code...

    class Account < ActiveRecord::Base
      has_one :contact
    
      accepts_nested_attributes_for :contact
    
      def after_create
        create_contact
      end
    end
    

    Note the addition of accepts_nested_attributes_for

    Should I open a new ticket for this? If I'm manually calling the assocation code with "create_contact" and not going through the accepts_nested_attributes_for avenue for creating the association, this seems to be "unexpected" behaviour.

  • CancelProfileIsBroken

    CancelProfileIsBroken May 25th, 2009 @ 09:14 PM

    Is that any different from what was fixed in #2249 ?

  • Mike Breen

    Mike Breen May 25th, 2009 @ 09:16 PM

    Andrew,

    I believe that this has been resolved.

  • Andrew Kaspick

    Andrew Kaspick May 25th, 2009 @ 09:27 PM

    It's a similar situation, but when using accepts_nested_attributes_for, line 211 in nested_attributes.rb causes the unexpected behaviour...

    reflection.options[:autosave] = true
    

    :autosave should not be true in my opinion for the example outlined. I spent a good number of hours trying to figure out why the association record was being saved even though it was invalid. Invalid records should not be saved to the DB.

    If I remove the accepts_nested_attributes_for line in my revised example, the issue goes away.

  • Daniel Guettler

    Daniel Guettler May 26th, 2009 @ 04:01 AM

    Ok, this is a rather engineered example in my view. You probably want to either have accepts_nested_attributes OR a manual create_after.
    Anyway so here what's happening:

    1) All validations for Account and associated Contact run and pass, Contact is nil at this point so no validations needed.

    2) Account is created etc.

    3) Callbacks run
    3.1) after_create callback is executed and tries to create a new Contact which in this step fails since validation doesn't pass. Account.contact is NOT nil anymore after this but contains the new record with validation errors.
    3.2) after_save callback is executed which will execute the callback added by autosave. It will create the Contact which was initialized in 3.1 without any further validations since it is assumed that the validations added by autosave have been run successfully.

    To avoid this one possible solution would be to not call save(false) in autosave_association.rb in order to avoid invalid records from being saved into the database.

  • Andrew Kaspick

    Andrew Kaspick May 26th, 2009 @ 04:46 AM

    I've actually modified my code to...

    def after_create
      c = build_contact
      c.save false
    end
    

    So yes, the original example given now could be looked upon as "engineered" as it doesn't check for errors anyway. I guess the "surprise" of having an object with errors being saved to the database as being the main issue here since it's what started my rat race of trying to find out why that was happening in the first place. A case of violating the principle of least surprise (POLS) I'd say.

    Thanks for the input.

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>

People watching this ticket

Pages