LoginSignup
10
5

More than 5 years have passed since last update.

Active Recordのhas_and_belongs_to_manyとhas_many :throughについてもくもく考える会その3【結論出す会】

Last updated at Posted at 2018-11-07

はじめに :snowman2:

こんばんは。
昨日は体調が悪すぎてリモート勤務が終わるとともに布団に入って寝てしまいました。
1日いちqiitaをやると決めて初めて何も投稿しない夜だったので朝起きて一番最初に大変残念に感じました。
でもよく寝たらすっきりしたので今日もぼちぼち書いていこうと思います。 :pen_ballpoint:
体調管理、気をつけていきたいです。 :bed:

今日書くこと :fish:

以前Active recordのassociationの扱いについて
has_and_belongs_to_manyhas_many :through についてのソースコードを読んだのですが、
その上で感じた「2つのメソッドの違い」と、「どちらを使ったほうがいいのか」ということをもくもくと考えてこのテーマについての記事は一区切りとしたいと思います。

2つのメソッドの違い :tent:

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つのメソッドの違いは、

①明示的に書けるかどうか
②(結合テーブルをもつか、開発者が管理するモデルの中で、結合モデルが存在するかどうか) :arrow_backward: コードを読まずとも最初からそういう違いというのはRailsガイドにて言及されている。

どちらを使ったほうがいいのか個人的な結論 :smiley:

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 を選びたいと思います。 :bow:

PS: やはりOSSのコードを読むというのは勉強になることが多い。量は調整しつつも続けて習慣化したい。

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5