はじめに
こんばんは。
昨日は体調が悪すぎてリモート勤務が終わるとともに布団に入って寝てしまいました。
1日いちqiitaをやると決めて初めて何も投稿しない夜だったので朝起きて一番最初に大変残念に感じました。
でもよく寝たらすっきりしたので今日もぼちぼち書いていこうと思います。
体調管理、気をつけていきたいです。
今日書くこと
以前Active recordのassociationの扱いについて
has_and_belongs_to_many
と has_many :through
についてのソースコードを読んだのですが、
その上で感じた「2つのメソッドの違い」と、「どちらを使ったほうがいいのか」ということをもくもくと考えてこのテーマについての記事は一区切りとしたいと思います。
2つのメソッドの違い
has_and_belongs_to_many
# Human(人間)とLanguage(言語)は多対多の関係を想定
class Human < ApplicationRecord
has_and_belongs_to_many :languages
end
class Language < ApplicationRecord
has_and_belongs_to_many :humans
end
has_many :through
# Human(人間)とLanguage(言語)は多対多の関係を想定
# Resume(履歴書)によって人と言語の関連状況を管理する。
class Human < ApplicationRecord
has_many :resumes
has_many :languages, through: :resumes
end
class Resume < ApplicationRecord
belongs_to :language
belongs_to :human
end
class Language < ApplicationRecord
has_many :resumes
has_many :humans, through: :resumes
end
このモデルを見比べるとわかるように has_and_belongs_to_many
の場合、中間モデルは存在していません。そのかわり、結合用のテーブルが存在するのですが、
「もくもく考えるかいその1」で書いたとおり、動的にクラスを作成しているという部分は、active record
がやってくれることなのでアプリケーション上のモデルの記述では(上記に記述したとおりだが) has_and_belongs_to_many をつけたという事実しか残らない。
その中間クラスの名前も 明示的にモデル内の記述で表現することには成功していない。
対して、 has_many :through
はモデルに情報が全て明示的に残されている。
例えば、 has_and_belongs_to_many
の場合、モデルの名前を変えたいと思った時にどうしたらいいだろうか?どのようなclass名が動的に生成されるかモデルに書かれた内容だけで想像できるだろうか?(私は個人的に一瞬おそらく答えに詰まる
2つのメソッドの違いは、
①明示的に書けるかどうか
②(結合テーブルをもつか、開発者が管理するモデルの中で、結合モデルが存在するかどうか) コードを読まずとも最初からそういう違いというのはRailsガイドにて言及されている。
どちらを使ったほうがいいのか個人的な結論
Railsガイド#belongs-toとhas-oneのどちらを選ぶか
にて既に言及されていることにかぶる部分もあるのですが、
自分は個人的には has_many :through を今後「多対多」に関しては使っていこうとこのもくもく会をやって思いました。
理由としては以下
has_and_belongs_to_many
-
内部で結局結合用のクラスを動的に作って処理をしていたという裏側を知ったこと(しかも明示的に書けた記憶はないままに勝手に
-
id(プライマリーキーの意味としたい)をもたせる事ができないので、indexをはることができない。レコード量が多いとき時間かかりそうな予感。。
-
ソフトウェアが将来どうなるか予想するのはとても難しい。少なくとも結合テーブルの属性が増える可能性は十分にある。でも
has_and_belongs_to_many
はそこを許容できない。 -
関連情報をレコードとしてわざわざ結合テーブルに入れる意味がある?という疑問
入れないといけない理由がすぐに見つけられない。
has_many :through
-
モデルが明示的に書けるのやはりいい。何か結合モデルに独自の属性を追加しようとしたときもできる。(
has_and_belongs_to_many
は関連を持つモデルのidをセットでレコードとして入れていくのでこの辺りの柔軟性がない。あくまでテーブル。モデルではない ) -
モデルなので、validation, もちろんcallbackもつかえる。
ソフトウェアは変化するもので、誰にもその将来の形は分からない。
なので、今できることは将来になるべく多くの選択肢を残すということではないかと思います。
その意味で「多対多」は has_many :through
を選びたいと思います。
PS: やはりOSSのコードを読むというのは勉強になることが多い。量は調整しつつも続けて習慣化したい。