2006-01-27 優しいRailsの育て方 [長年日記]
● [Rails] habtm と has_many :through (ActiveRecord)
habtm は多対多の関係にある2つのモデルを表現するときに非常に便利です。
テーブル (habtm)
|
また、その関係は2つのモデルが各々結合用モデルに対して has_many を持っている構造とも同じです。
テーブル (has_many)
|
このように、2つのモデルが多対多の関係を持つ場合、has_many と habtm はどう違うのでしょうか?DB的には、上記の通りどちらもモデル用の2テーブルと結合を表す1テーブルのみで、違いはありません。(主キーはおいといて)。
● has_many と habtm の違い
結局のところ、両者の違いは ActiveRecord が提供してくれるDB操作メソッドだけなのです。
参照(habtm)
|
参照(has_many)
|
● habtm のメリット
habtm は「2つのモデル、かつ、関係性のみ」に特化している分、汎用的な has_many に比べると
- 関連するモデルへ直接アクセスするメソッドがある
- 関連に対する作成・削除の操作メソッドが充実している
というメリットがあります。しかし、
- 結合テーブルに追加情報を持たせて色々やりたい
- 2個以上のモデルを関連付けたい
といった複雑なケースには不向きです。こういう場合には、結合テーブルをモデルとして表に出したhas_many による構成がよいでしょう。
● 関連先のモデルへのアクセス
しかし、
実行(habtm)
>> berryz = Group.create(:name=>"Berryz工房")
>> maiha = Member.create(:name=>"石村舞波")
# 関係を作成
>> berryz.members << maiha
# 所属メンバーの一覧
>> berryz.members
=> [#<Member:0xb7a9dc9c @attributes={"name"=>"石村舞波", "id"=>"8"}>] |
一度この habtm のエレガントなアクセス方法に慣れてしまうと、
実行(has_many)
>> berryz = Group.create(:name=>"Berryz工房")
>> maiha = Member.create(:name=>"石村舞波")
# 関係を作成
>> Assign.create(:group=>berryz, :member=>maiha)
# 所属メンバーの一覧
>> berryz.assigns.collect{|assign| assign.member}
=> [#<Member:0xb7a9dc9c @attributes={"name"=>"石村舞波", "id"=>"8"}>] |
has_many で実現された多対多での操作は苦痛です。
● そこで DHH は考えた
現在の svn trunk では、has_many に :through オプションが導入され、このような中間モデル(Assign)を指定することで、habtm のように直接的に対象となる関連モデルを参照可能になりました。
Model(has_many :through)
class Group < ActiveRecord::Base has_many :assigns has_many :members, :through=>:assigns end class Member < ActiveRecord::Base has_many :assigns has_many :groups, :through=>:assigns end class Assign < ActiveRecord::Base belongs_to :group belongs_to :member end |
参照(has_many :throgh)
|
実行(has_many :through)
# 所属メンバーの一覧
>> berryz.members
=> [#<Member:0xb7a9dc9c @attributes={"name"=>"石村舞波", "id"=>"8"}>] |
現在はまだ参照のみで、追加や削除はまだ habtm の利便性に遠く及びませんが、どんどん機能追加されていくと思います。そして、中間モデル(Assign)は ActiveRecord ですので、好きなように扱うことができます。これは habtm に対する絶対的なメリットでしょう。
● 今後の予想
ここからは完全に主観と想像ですが、流れというか開発の勢いというか方向性は habtm よりも has_many の方に完全にシフトしている気がします。理由は5つ。
- habtm も has_many も機能的には似ている
- habtm は主キーを持たないので AR 上で使うには限界がある (将来的な機能拡張的にも必ず壁にぶつかる)
- 中間モデルで表現できることによるメリットも多いので has_many の方が将来性もある
- ticket 的に habtm は結構放置されている (count 不具合、pkey 拡張)
- DHH 自身が habtm よりも has_many で中間モデルで表現する方に興味を示している (ように見える)
恐らくこれからも has_many :through がさらに機能拡張されていき、Rails 1.2 ぐらいでは、habtm は has_many :through へのマクロになってると予想します。
と、has_many を紹介しておいてなんですが、個人的には habtm のエレガントさにベタ惚れなので、has_many で同等の機能ができる日が来るまで、habtm を愛し続けるつもりです。こっそり「はびたむ」と読んでるくらいラブです。はびたむで気持ちは一杯です。もう habtm MAX Heart です。正直、歌いながら使ってます。(はびったむっ!はびったむっ!はずめにで〜、はびたむ〜、ふ〜たりは〜、はびたむ〜♪)
● 参考
- [Wiki] ThroughAssociations http://wiki.rubyonrails.org/rails/pages/ThroughAssociations
- http://article.gmane.org/gmane.comp.lang.ruby.rails/32742/
- http://www.matthewman.net/articles/2006/01/06/rails-activerecord-goes-through
- http://www.infused.org/2005/12/06/has-many-through-association/
- http://rails.techno-weenie.net/tip/2005/12/23/teaching_your_blog_model_new_tricks_with_has_many_through
- [Patch] :through with :include http://dev.rubyonrails.org/ticket/3611
- [Defect] count http://dev.rubyonrails.org/ticket/3528
- [Defect] pkey in habtm http://dev.rubyonrails.org/ticket/1031


vgdj laiht gwkchqjp tyxjleazk tkbx smzfli tifdansj
ここを見て、has_many :throughにassignsを使うようにしたら、test/functionalsでエラーになってしまいました。<br>インスタンス変数のためのメソッドとしてassignsは定義されてるから、モデル名としては使わないほうがいいみたいでした。<br>Rails 1.xのときは気づかなかったので、Rails 2.0からでしょうか?