how do i save multiple activerecord objects that have references to other instances of the same class?

What you are describing is called a self-referential assocation or a self join.

To setup a self referential assocation you just create a nullable foreign key column that points to the same table:

class CreateObservations < ActiveRecord::Migration[6.0]
  def change
    add_reference :comments, :parent, 
      null: true,
      foreign_key: { to_table: :comments }
  end
end

And an assocation that points back to the same class:

class Comment < ApplicationRecord
  belongs_to :parent, 
             class_name: 'Comment',
             optional: true,
             inverse_of: :children
             
  has_many :children,
             class_name: 'Comment',
             foreign_key: 'parent_id',
             inverse_of: :parent
end

If you don’t make the column nullable and the belongs_to assocation optional you end up in a chicken vs egg scenario where you can’t actually insert the first record in the table as it has nothing to refer to.

If you want to create a record and children at the same you use accepts_nested_attributes.

class Comment < ApplicationRecord
  belongs_to :parent, 
             class_name: 'Comment',
             optional: true,
             inverse_of: :children
             
  has_many :children,
             class_name: 'Comment',
             foreign_key: 'parent_id',
             inverse_of: :parent

  accepts_nested_attributes_for :children
end

This lets you create a reddit style thread of comments with:

Comment.create!(
  text: "You suck!!!!!", 
  user_id: 99, 
  children_attributes: [
    { 
      text: "Be respectful, or I'll block you.", 
      user_id: 1,
      children_attributes: [
        {
          text: "Typical *******, doesn't belive in free speech.",
          user_id: 99
        }
      ]
    }
  ]
])

As it automatically handles recursion. See the guides for how to to create forms for nested attributes and how to whitelist them in your controller. If the nested attributes contain an id the nested records will be updated instead of creating new records.

Dealing with the whole post_id issue can be done in different ways:

  • Use a callback to set the post from the parent comment. This is my least favorite.
  • Make the column nullable and get the parent by traversing up the tree.
  • Use a polymorphic assocation instead so that a comment can belong to either a comment or a post. Get the original post by traversing up the tree.

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top