Kapan harus menggunakan relasi “has_many: through” di Rails?

123

Saya mencoba memahami apa has_many :throughitu dan kapan menggunakannya (dan bagaimana). Namun, saya tidak mengerti. Saya membaca Beginning Rails 3 dan saya mencoba Googling, tetapi saya tidak dapat memahami.

Luke yang beruntung
sumber

Jawaban:

177

Katakanlah Anda memiliki dua model: Userdan Group.

Jika Anda ingin pengguna menjadi bagian dari grup, maka Anda dapat melakukan sesuatu seperti ini:

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

Bagaimana jika Anda ingin melacak metadata tambahan di sekitar pengaitan? Misalnya, ketika pengguna bergabung dengan grup, atau mungkin apa peran pengguna dalam grup?

Di sinilah Anda menjadikan pengaitan sebagai objek kelas pertama:

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

  # has attributes for date_joined and role
end

Ini memperkenalkan tabel baru, dan menghilangkan group_idkolom dari tabel pengguna.

Masalah dengan kode ini adalah Anda harus memperbarui setiap tempat lain Anda menggunakan kelas pengguna dan mengubahnya:

user.groups.first.name

# becomes

user.group_memberships.first.group.name

Jenis kode ini menyebalkan, dan membuat perubahan seperti ini menyakitkan.

has_many :through memberi Anda yang terbaik dari kedua dunia:

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

Sekarang Anda dapat memperlakukannya seperti biasa has_many, tetapi dapatkan manfaat dari model pengaitan saat Anda membutuhkannya.

Perhatikan bahwa Anda juga dapat melakukan ini dengan has_one.

Edit: Memudahkan untuk menambahkan pengguna ke grup

def add_group(group, role = "member")
  self.group_associations.build(:group => group, :role => role)
end
Ben Scheirman
sumber
2
Di sinilah saya akan menambahkan metode pada usermodel untuk menambahkan grup. Sesuatu seperti hasil edit yang baru saja saya buat. Semoga ini membantu.
Ben Scheirman
Begitu, apakah saya juga harus menulis user.groups << group? Ataukah semuanya ditangani oleh asosiasi ini?
LuckyLuke
Saya memiliki kelas yang sama dengan group_membership dan saya mengalami masalah saat menggunakan gadis pabrik, maukah Anda membantu saya?
Ayman Salah
Saya akan menggunakan "has_many: group_memberships". Menggunakan singular akan berhasil, tetapi user.group_membership akan menjadi collection dan user.group_memberships tidak akan berfungsi.
calasyr
Itu salah ketik. Tetap.
Ben Scheirman
212

Katakanlah Anda memiliki model ini:

Car
Engine
Piston

Sebuah mobil has_one :engine
Sebuah mesin belongs_to :car
Sebuah mesin has_many :pistons
Pistonbelongs_to :engine

has_many :pistons, through: :engine
Piston mobilhas_one :car, through: :engine

Pada dasarnya Anda mendelegasikan hubungan model ke model lain, jadi daripada harus menelepon car.engine.pistons, Anda bisa melakukannyacar.pistons

cpuguy83
sumber
9
Ini sangat masuk akal!
RajG
24
Saya menyebutnya SACA - ShortAndClearAnswer.
Asme Hanya
18

Tabel Gabungan ActiveRecord

has_many :throughdan has_and_belongs_to_manyhubungan berfungsi melalui tabel gabungan , yang merupakan tabel perantara yang mewakili hubungan antara tabel lain. Tidak seperti kueri JOIN, data sebenarnya disimpan dalam tabel.

Perbedaan Praktis

Dengan has_and_belongs_to_many, Anda tidak memerlukan kunci utama, dan Anda mengakses rekaman melalui relasi ActiveRecord daripada melalui model ActiveRecord. Anda biasanya menggunakan HABTM saat ingin menghubungkan dua model dengan hubungan banyak-ke-banyak.

Anda menggunakan has_many :throughhubungan saat Anda ingin berinteraksi dengan tabel gabungan sebagai model Rails, lengkap dengan kunci utama dan kemampuan untuk menambahkan kolom kustom ke data yang digabungkan. Yang terakhir ini sangat penting untuk data yang relevan dengan baris yang digabungkan, tetapi tidak benar-benar milik model terkait - misalnya, menyimpan nilai terhitung yang berasal dari bidang di baris yang digabungkan.

Lihat juga

Dalam A Guide to Active Record Associations , rekomendasinya berbunyi:

Aturan praktis yang paling sederhana adalah Anda harus menyiapkan hubungan has_many: through jika Anda perlu menggunakan model hubungan sebagai entitas independen. Jika Anda tidak perlu melakukan apa pun dengan model hubungan, mungkin lebih mudah untuk menyiapkan hubungan has_and_belongs_to_many (meskipun Anda harus ingat untuk membuat tabel penggabungan dalam database).

Anda harus menggunakan has_many: through jika Anda memerlukan validasi, callback, atau atribut tambahan pada model gabungan.

Todd A. Jacobs
sumber