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.

association#build inconsistency

#990

When building an object through an association, the resulting object isn't bound to the proxy owner if the proxy owner is a new record. If the proxy owner is already saved, it is bound.

I think this is inconsistent.

This also boils down to using find_or_initialize_by or find_or_create_by on associations on new records.


class User < ActiveRecord::Base
  has_many :posts, :dependent => :destroy
end

class Post < ActiveRecord::Base
  belongs_to :user
end

>> u=User.first
=> #<User id: 1, name: "user 1", created_at: "2008-09-07 22:07:35", updated_at: "2008-09-07 22:07:35">
>> u2=User.new
=> #<User id: nil, name: nil, created_at: nil, updated_at: nil>

>> u.posts.build.user
=> #<User id: 1, name: "user 1", created_at: "2008-09-07 22:07:35", updated_at: "2008-09-07 22:07:35">
>> u2.posts.build.user
=> nil

>> u.posts.find_or_initialize_by_title('title').user
=> #<User id: 1, name: "user 1", created_at: "2008-09-07 22:07:35", updated_at: "2008-09-07 22:07:35">
>> u2.posts.find_or_initialize_by_title('title').user
=> nil

Reported by Mathijs Kwik · September 7th, 2008 @ 11:22 PM

State: resolved
Milestone: 3.x
Assigned to: Michael Koziarski Michael Koziarski
Importance: Low

Activity

  1. Pratik
    Pratik
    • Assigned user set to Michael Koziarski
    • State changed from new to wontfix

    Please reopen once we have a patch for this.

    This looks similar to an ID map patch Koz has been involved with, so assigning the ticket to him.

    December 20th, 2008 @ 03:32 PM

  2. Prem Sichanugrist (sikachu)
    Prem Sichanugrist (sikachu)

    I just wonder ..

    I've looked at the code, and I found out that the association's build method is done by setting foreign_key to be the parent's id. Can we also do something like set the attribute with the name of the parent object to be refer to parent object?

    February 1st, 2009 @ 06:24 AM

  3. Ryan Bigg
    Ryan Bigg
    • State changed from wontfix to open

    Marked a similar ticket, #4497 ActiveRecord associations .build does not set associations., as a duplicate of this one and asked the user to submit a patch here.

    April 29th, 2010 @ 12:52 AM

  4. Evgeniy Dolzhenko
    Evgeniy Dolzhenko

    Btw. shouldn't :inverse_of association options handle this?

    April 29th, 2010 @ 07:08 AM

  5. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.x to 3.x

    May 4th, 2010 @ 06:48 PM

  6. Andreas Mayer
    Andreas Mayer

    Have the same problem. Using the workaround. Will this be fixed in Rails 3 final?

    July 31st, 2010 @ 12:05 AM

  7. Neeraj Singh
    Neeraj Singh
    • State changed from open to resolved
    • Importance changed from to Low

    As Evgeniy Dolzhenko suggested setting inverse_of resolves the issue.

    class Car < ActiveRecord::Base
      has_many :brakes, :inverse_of => :car
    end
    
    class Brake < ActiveRecord::Base
      belongs_to :car, :inverse_of => :brakes
    end
    
    > Car.first.brakes.build.car
      Car Load (0.4ms)  SELECT "cars".* FROM "cars" LIMIT 1
     => #<Car id: 1, name: nil> 
    
    > Car.new.brakes.build.car
     => #<Car id: nil, name: nil> 
    
    > Car.create.brakes.build.car
      SQL (0.5ms)  INSERT INTO "cars" ("name") VALUES (NULL)
     => #<Car id: 2, name: nil>
    
    
    > Car.last.brakes.find_or_initialize_by_name('cool').car
      Car Load (0.6ms)  SELECT "cars".* FROM "cars" ORDER BY cars.id DESC LIMIT 1
      Brake Load (0.2ms)  SELECT "brakes".* FROM "brakes" WHERE ("brakes".car_id = 2) AND ("brakes"."name" = 'cool') LIMIT 1
      Car Load (0.1ms)  SELECT "cars".* FROM "cars" WHERE ("cars"."id" = 2) LIMIT 1
     => #<Car id: 2, name: nil> 
    
    > Car.last.brakes.find_or_create_by_name('cool').car
      Car Load (0.4ms)  SELECT "cars".* FROM "cars" ORDER BY cars.id DESC LIMIT 1
      Brake Load (0.3ms)  SELECT "brakes".* FROM "brakes" WHERE ("brakes".car_id = 2) AND ("brakes"."name" = 'cool') LIMIT 1
      SQL (0.3ms)  INSERT INTO "brakes" ("car_id", "name") VALUES (2, 'cool')
     => #<Car id: 2, name: nil>
    

    tested with rails edge.

    August 13th, 2010 @ 03:03 PM