はじめに
自己結合のサンプルとしてディレクトリツリーの表現を考える。
備忘録になってしまっているので、まとめ直したいところ。
ディレクトリツリーの表現
ディレクトリツリーをモデルで表現する場合以下のような設定を行う。
class Directory < ActiveRecord::Base
has_one :parent, through: :parent_tree, source: :parent
has_one :parent_tree, class_name: "DirectoryTree", foreign_key: :child_id
has_many :children, through: :child_trees, source: :child
has_many :child_trees, class_name: "DirectoryTree", foreign_key: :parent_id
end
class DirectoryTree < ActiveRecord::Base
belongs_to :parent, class_name: "Directory"
belongs_to :child, class_name: "Directory"
end
@directory にDirectoryモデルのインスタンスが格納されているとして、
親ディレクトリの関連(DirectorTree)は @directory.parent_tree で取得。
親ディレクトリ自体(Directory)は @directory.parent で取得。
この説明だけで、定義の意味を理解できる人はどれだけいるだろうか。
個人的に定義が素直に頭に入らない理由が2つある。
1つ目: foreign_keyが自身と関連先のモデルのどちらを指しているのか分からないから
答えとしては、
- belongs_toで定義するforeign_keyは自身のカラムを指定。
- has_manyで定義するforeign_keyは関連先のモデルのカラムを指定。
だが、理解するのに大分時間がかかった。
なおhas_manyの場合、foreign_keyの設定がないと、関連付ける外部キーの名前は「自身モデル名_id(例ではdirectory_id)」となるが、
参照先のモデルにはDrectoryモデルのidを示すカラムがparent_idとchild_idとして分けて定義しているので、どちらに紐づくのかを指定する必要がある。
この必要性を理解すればhas_manyのforeign_keyは関連先のモデルのカラムを指定することが自明なのだが、なかなか考えがまとまらなかった。。。
2つ目: sourceが自身と関連先のモデルのどちらを指しているのか分からないから
答えとしては、
- 関連先のモデルを指定
だが、sourceという言葉自体に惑わされると理解が難しい。
以下のように定義を簡略化すれば、
配下のディレクトリを取得する場合には「:child_trees」を通して「:directory」を取得するという流れがわかりやすいかも。
class Directory < ActiveRecord::Base
has_many :children, through: :child_trees, source: :directory
has_many :child_trees, class_name: "DirectoryTree", foreign_key: :parent_id
end
class DirectoryTree < ActiveRecord::Base
belongs_to :directory
end
参考
- マイグレーション時の外部キー制約指定方法