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.

has_one through associations not saved correctly

#3480

Summary

Attributes created using has_one :through associations are lost upon serialization to the database.

Details

class Club < ActiveRecord::Base
  has_one :membership
end
class Membership < ActiveRecord::Base
  belongs_to :person
  belongs_to :club
end
class Person < ActiveRecord::Base
  has_one :membership
  has_one :club, :through => :membership
end
require 'test_helper'

class PersonTest < ActiveSupport::TestCase
  test "person's club is saved" do
    p = Person.new
    c = Club.new
    p.club = c
    assert_equal c, p.club
    c.save!; c.reload
    p.save!; p.reload
    assert_equal c, p.club
  end
end
$ ruby -Itest test/unit/person_test.rb
Loaded suite test/unit/person_test
Started
F
Finished in 0.057744 seconds.

  1) Failure:
test_person's_club_is_saved(PersonTest) [test/unit/person_test.rb:11]:
<#<Club id: 1, created_at: "2009-11-11 01:01:47", updated_at: "2009-11-11 01:01:47">> expected but was
<nil>.

1 tests, 2 assertions, 1 failures, 0 errors

Reported by gerad · November 11th, 2009 @ 12:15 AM

State: invalid
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. gerad
    gerad

    Also happens on 2.3.4 version 7ba80252a5a6bd94c8445347caee663b12c1a526

    November 11th, 2009 @ 01:03 AM

  2. Jigar Patel
    Jigar Patel

    Three cases -
    1. p = Person.new
    c = Club.new p.club = c This line will insert a new row in memberships table with both person_id and club_id as nil.
    c.save p.save Both objects will be saved without any related membership. Hence your test case fails.

    1. p = Person.new
      c = Club.new c.save p.club = c

    This will create an entry in Membership table with club_id = "c.id" and person_id as nil
    p.save will save the object but without any related membership and hence without any related organization.

    1. p = Person.new
      p.save p will be a Person with some id.
      c = Club.new c.save c will be a Club with some id.
      p.club = c This will create an entry in memberships table with person_id and club_id pointing to corresponding rows in persons and clubs tables.
      p.save will save and you will be able to fetch club.

    July 24th, 2010 @ 12:58 PM

  3. lakshmanan
    lakshmanan

    I ran your testcase statements in console and I test Membership after every statement. You are not saving anything

    In short -> you are playing in memory without saving anything. so literally you are playing with nils

    >> p = Person.new
    => #<Person id: nil, created_at: nil, updated_at: nil>
    >> Membership.last
    => nil
    >> c = Club.new
    => #<Club id: nil, created_at: nil, updated_at: nil>
    >> Membership.last
    => nil
    >> p.club = c
    => #<Club id: nil, created_at: nil, updated_at: nil>
    >> Membership.last
    => nil
    >> c.save!
    => true
    >> Membership.last
    => nil
    >> c.reload
    => #<Club id: 2, created_at: "2010-07-24 11:57:24", updated_at: "2010-07-24 11:57:24">
    >> p.save!
    
    # after this statement - your test will pass
    
    => true
    >> Membership.last
    => #<Membership id: 2, club_id: nil, person_id: 2, created_at: "2010-07-24 11:57:43", updated_at: "2010-07-24 11:57:43">
    

    Hope it helps

    July 24th, 2010 @ 01:08 PM

  4. Rohit Arondekar
    Rohit Arondekar
    • State changed from new to invalid
    • Importance changed from to Low

    p.club and c are not the same. c is created when c is saved. While p.club is created when p is saved.

    September 27th, 2010 @ 02:52 PM