Andrew
Andrew

Reputation: 43153

Rails has_many / has_one same model

So, I have two models, Collection and Folder.

Within each Collection there is one root folder. Folders all belong to a Collection, but can also be nested within each other.

Following this question, I wrote my models like shown below. I also added a callback, because I always want a Collection to start out with a root folder.

class Folder < ActiveRecord::Base
  has_one :master_collection, :class_name => 'Collection', :foreign_key => :root_folder_id
  belongs_to :collection
  belongs_to :parent, :class_name => 'Folder'
  has_many :subfolders, :class_name => 'Folder', :foreign_key => :parent_id
  ...
end

class Collection < ActiveRecord::Base
  belongs_to :root_folder, :class_name => 'Folder'
  has_many :folders

  after_create :setup_root_folder

  private
  def setup_root_folder
    self.root_folder = Folder.create(:name => 'Collection Root', :collection => self )
  end
end

In Folders I have the columns: parent_id, folder_id In Collections I have the column: root_folder_id

This seems like it should work, but I get this strange behavior in the console:

ruby-1.9.2-p0 :001 > c = Collection.create(:name=>"Example")
 => #<Collection id: 6, name: "Example", ...timestamps..., root_folder_id: 8> 
ruby-1.9.2-p0 :003 > f = c.root_folder
 => #<Folder id: 8, parent_id: nil, name: "Collection Root", ...timestamps..., collection_id: 6> 
ruby-1.9.2-p0 :004 > f.master_collection
 => nil 

So, clearly the association works on the collection side, but the root folder is not able to find the collection it is the root of, even though the foreign key is available and ActiveRecord isn't raising any errors on using the association...

Any ideas?

Upvotes: 2

Views: 3136

Answers (1)

Xavier Holt
Xavier Holt

Reputation: 14619

I suspect that this is what's happening:

  1. You create Collection C
  2. C is saved to the database
  3. Rails calls C.after_create
  4. You create Folder F
  5. F is saved to the database
  6. C's root_folder_id is set to F's id
  7. This change is NOT saved to the database
  8. You call F.master_collection
  9. F queries the database, looking for Collections with coleections.root_folder_id = folders.id
  10. Since C's new root_folder_id hasn't been saved, F doesn't find anything

If you want to test this, call c.save in your example code before you call f.master_collection - and you should get the collection, like you expect (you might need a f.reload too).

That said, I've never liked double belongs_tos (Folder belongs_to Collection + Collection belongs_to root_folder). Instead, I'd recommend:

class Collection < ActiveRecord::Base
    has_one :root_folder, :class_name => 'Folder', :conditions => {:parent_id => nil}

    # ...other stuff...
end

Hope that helps!

PS: Your definition of Folder.master_collection will only give you back a collection if you call it from a root folder - all other folders will just return nil, since no collections have root_folder_ids that point to them. Is that what you intended?

Upvotes: 2

Related Questions