テーブル構成例

[Nodesテーブル] 最上位のノード以外は親ノードを持っている

id name parent_id
1 parrent_node null
2 child_node_1 1
3 child_node_2 1
4 child_node_1_1 2
5 child_node_1_2 2
6 child_node_2_1 3
7 child_node_2_2 3

確認環境

  • ruby: ruby 2.5.3
  • rails: rails 5.2.2

実際のコード

class Node < ApplicationRecord
  has_many :children, class_name: "Node", foreign_key: "parrent_id"

  belongs_to :parrent, class_name: "Node", optional: true # rails5 から default が require になっているため `optional: true` を指定しないと `nil` での保存が不可
end
class CreateNodes < ActiveRecord::Migration[5.2]
  def change
    create_table :nodes do |t|
      t.string :name
      t.references :parrent

      t.timestamps
    end
  end
end

実行例

テーブル構成例のデータが格納済の状態

# 最上位ノードに紐付く子ノードのレコードを取得
parrent_node = Node.first
parrent_node.children
#=> Node Load (0.8ms)  SELECT  `nodes`.* FROM `nodes` WHERE `nodes`.`parrent_id` = 1 LIMIT 11
#=> #<ActiveRecord::Associations::CollectionProxy [#<Node id: 2, name: "child_node_1", parrent_id: 1, created_at: "2019-02-21 09:05:38", updated_at: "2019-02-21 09:05:38">, #<Node id: 3, name: "child_node_2", parrent_id: 1, created_at: "2019-02-21 09:05:38", updated_at: "2019-02-21 09:05:38">]>

# 子ノードに紐付く親ノードのレコードを取得
child_node = Node.find(2)
child_node.parrent
#=> Node Load (0.5ms)  SELECT  `nodes`.* FROM `nodes` WHERE `nodes`.`id` = 1 LIMIT 1
#=> #<Node id: 1, name: "parrent_node", parrent_id: nil, created_at: "2019-02-21 09:05:37", updated_at: "2019-02-21 09:05:37">

# 最上位ノードに紐付く親ノードのレコードを取得
parrent_node = Node.first
parrent_node.parrent
#=> nil