web-dev-qa-db-ja.com

Railsで「has_many:through」リレーションを使用する必要があるのはいつですか?

私はhas_many :throughとは何か、それをいつ使用するか(およびその方法)を理解しようとしています。しかし、私はそれを得ていません。 Beginning Rails 3を読んで、グーグルで試しましたが、理解できません。

115
LuckyLuke

UserGroupの2つのモデルがあるとします。

ユーザーをグループに所属させるには、次のようにします。

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

関連付けに関する追加のメタデータを追跡する場合はどうなりますか?たとえば、ユーザーがグループに参加したとき、またはグループ内のユーザーの役割は何ですか?

これは、関連付けをファーストクラスオブジェクトにする場所です。

class GroupMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group

  # has attributes for date_joined and role
end

これにより、新しいテーブルが導入され、ユーザーのテーブルからgroup_id列が削除されます。

このコードの問題は、ユーザークラスを使用する他のすべての場所を更新し、変更する必要があることです。

user.groups.first.name

# becomes

user.group_memberships.first.group.name

このタイプのコードはひどいものであり、このような変更を導入するのは苦痛です。

has_many :throughは両方の長所を提供します:

class User < ActiveRecord::Base
  has_many :groups, :through => :group_memberships  # Edit :needs to be plural same as the has_many relationship   
  has_many :group_memberships
end

これで、通常のhas_manyのように扱うことができますが、必要なときに関連付けモデルの利点を得ることができます。

has_oneを使用してこれを行うこともできます。

編集:ユーザーをグループに簡単に追加できるようにする

def add_group(group, role = "member")
  self.group_associations.build(:group => group, :role => role)
end
170
Ben Scheirman

これらのモデルがあるとします:

Car
Engine
Piston

has_one :engine
エンジンbelongs_to :car
エンジンhas_many :pistons
ピストンbelongs_to :engine

has_many :pistons, through: :engine
ピストンhas_one :car, through: :engine

基本的に、モデルの関係を別のモデルに委任しているので、car.engine.pistonsを呼び出す代わりにcar.pistonsを実行できます。

187
cpuguy83

ActiveRecordの結合テーブル

has_many :throughおよびhas_and_belongs_to_many関係は、join tableを通じて機能します。これは、他のテーブル間の関係を表す中間テーブルです。 JOINクエリとは異なり、データは実際にはテーブルに保存されます。

実用的な違い

has_and_belongs_to_manyを使用すると、主キーは不要であり、ActiveRecordモデルではなくActiveRecordリレーションを介してレコードにアクセスします。通常、多対多の関係で2つのモデルをリンクする場合は、HABTMを使用します。

Railsモデルとして結合テーブルとやり取りしたい場合は、has_many :through関係を使用します。これは、主キーと結合データにカスタム列を追加する機能を備えています。後者は、結合された行に関連するデータにとって特に重要ですが、実際には関連モデルに属しません。たとえば、結合された行のフィールドから派生した計算値を保存します。

こちらもご覧ください

A Active Record Associationsへのガイド では、推奨事項は次のとおりです。

最も簡単な経験則は、関係モデルを独立したエンティティとして使用する必要がある場合は、has_many:through関係を設定することです。関係モデルで何もする必要がない場合は、has_and_belongs_to_many関係をセットアップする方が簡単かもしれません(ただし、データベースに結合テーブルを作成することを忘れないでください)。

結合モデルで検証、コールバック、または追加の属性が必要な場合は、has_many:throughを使用する必要があります。

16
Todd A. Jacobs