私はhas_many :through
とは何か、それをいつ使用するか(およびその方法)を理解しようとしています。しかし、私はそれを得ていません。 Beginning Rails 3を読んで、グーグルで試しましたが、理解できません。
User
とGroup
の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
これらのモデルがあるとします:
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
を実行できます。
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を使用する必要があります。