This project is archived and is in readonly mode.

#3480 ✓invalid
gerad

has_one through associations not saved correctly

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

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

Comments and changes to this ticket

  • gerad

    gerad November 11th, 2009 @ 01:03 AM

    Also happens on 2.3.4 version 7ba80252a5a6bd94c8445347caee663b12c1a526

  • Jigar Patel

    Jigar Patel July 24th, 2010 @ 12:58 PM

    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.
  • lakshmanan

    lakshmanan July 24th, 2010 @ 01:08 PM

    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

  • Rohit Arondekar

    Rohit Arondekar September 27th, 2010 @ 02:52 PM

    • 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.

  • csnk

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