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_many .build does not set reverse reflection

#3510

class Category < ActiveRecord::Base

  has_many :things, :dependent => :destroy
end

class Thing < ActiveRecord::Base
  belongs_to :category
  validates_presence_of :category
end


>> c=Category.new
>> t=c.things.build
>> c.save
=> false
>> pp c.errors
#<ActiveRecord::Errors:0x7f24100e2528
 @base=#<Category id: nil, created_at: nil, updated_at: nil, name: nil>,
 @errors=
  {"things"=>
    [#<ActiveRecord::Error:0x7f24100cdbc8
      @attribute=:things,
      @base=#<Category id: nil, created_at: nil, updated_at: nil, name: nil>,
      @message=:invalid,
      @options={},
      @type=:invalid>]}>
=> nil

This seems very wrong to me. Things has a category, it was created through a category!

I certainly expect t.category_id to return nil. However, I would expect t.category to return the category. To work around the problem you have to insert a

>> t.category = c

before the save. Which is probably fine for most people. But in my code, that ends up being:

>> record.send("#{owner.class.reverse_reflection(association_name).name}=", owner)

So I went "UGH!" and opened this bug.

Reported by Bryan Larsen · November 24th, 2009 @ 09:29 PM

State: resolved
Milestone: none
Assigned to: Neeraj Singh Neeraj Singh
Importance: none

Activity

  1. Mike Gehard
    Mike Gehard
    • Importance changed from to

    Here is a failing test for the above situation. Next step is to wade through the code to figure out where to implement the fix. I wanted to get the failing test up here in case someone else with more knowledge of ActiveRecord wanted to fix it. :-)

    July 27th, 2010 @ 01:59 AM

  2. Mike Gehard
  3. Mike Gehard
    Mike Gehard

    One workaround for this is to remove the validates_presence_of :category and rely on a foreign key constraint on category_id (NOT NULL). This is OK as long as you are creating the Thing through the Category because you know that the Category exists because you just created it.

    July 27th, 2010 @ 09:04 PM

  4. Neeraj Singh
    Neeraj Singh
    • State changed from new to open
    • Assigned user set to Neeraj Singh
    • Tag set to rails 3, activerecord

    I think it is a valid concern. I will take a look at it. It might be a bit tricky but will see how it goes.

    July 27th, 2010 @ 09:40 PM

  5. Neeraj Singh
    Neeraj Singh
    • State changed from open to resolved

    There is another ticket open (can't find right now) which arges that :inverse_of should be the default. Code below fixes the issue

    class Car < ActiveRecord::Base
      has_many :brakes, :inverse_of => :car
    
      def self.lab
        c = Car.new
        b = c.brakes.build
        puts b.car
      end
    
    end
    
    class Brake < ActiveRecord::Base
      belongs_to :car, :inverse_of => :brakes
    
      def self.lab
      end
    
    end
    
    > Car.lab
    #<Car:0x10203bc38>
    

    August 10th, 2010 @ 02:57 PM

  6. Mike Gehard
    Mike Gehard

    Neeraj,

    Can you point me in the direction of the commit that fixed this? I would like to take a look to see how you fixed it as a learning experience.

    Thanks,
    Mike

    August 10th, 2010 @ 03:40 PM

  7. Neeraj Singh