เมื่อใดควรใช้ความสัมพันธ์“ has_many: through” ใน Rails?


123

ฉันพยายามทำความเข้าใจว่าอะไรhas_many :throughคืออะไรและจะใช้เมื่อใด (และอย่างไร) อย่างไรก็ตามฉันไม่เข้าใจ ฉันกำลังอ่าน Beginning Rails 3 และพยายามใช้ Googling แต่ฉันไม่เข้าใจ

คำตอบ:


177

สมมติว่าคุณมีสองรุ่น: UserและGroup.

หากคุณต้องการให้ผู้ใช้อยู่ในกลุ่มคุณสามารถดำเนินการดังนี้:

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

2
นี่คือที่ที่ฉันจะเพิ่มวิธีการในuserโมเดลเพื่อเพิ่มกลุ่ม บางอย่างเช่นการแก้ไขที่ฉันเพิ่งทำ หวังว่านี่จะช่วยได้
Ben Scheirman

ฉันเข้าใจฉันต้องเขียนด้วยuser.groups << groupหรือไม่? หรือทุกอย่างจัดการโดยสมาคมนี้?
LuckyLuke

ฉันมีคลาสที่เหมือนกับ group_membership และฉันมีปัญหาในการใช้สาวโรงงานด้วยคุณจะช่วยฉันได้ไหม
Ayman Salah

ฉันจะใช้ "has_many: group_memberships" การใช้เอกพจน์จะได้ผล แต่ user.group_membership จะรวบรวมและ user.group_memberships จะไม่ทำงาน
calasyr

นั่นเป็นการพิมพ์ผิด แก้ไขแล้ว.
Ben Scheirman

212

สมมติว่าคุณมีโมเดลเหล่านี้:

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


9
นี่เป็นเรื่องที่สมเหตุสมผลมาก!
RajG

24
ฉันเรียกสิ่งนี้ว่า SACA - ShortAndClearAnswer
Asme เพิ่ง

18

ตารางเข้าร่วม ActiveRecord

has_many :through และ has_and_belongs_to_manyฟังก์ชันความสัมพันธ์ผ่านตารางเข้าร่วมซึ่งเป็นตารางกลางที่แสดงถึงความสัมพันธ์ระหว่างตารางอื่น ๆ ซึ่งแตกต่างจากแบบสอบถาม JOIN ข้อมูลจะถูกเก็บไว้ในตาราง

ความแตกต่างในทางปฏิบัติ

กับ has_and_belongs_to_manyคุณไม่จำเป็นต้องมีคีย์หลักและคุณเข้าถึงเรกคอร์ดผ่านความสัมพันธ์ของ ActiveRecord แทนที่จะใช้แบบจำลอง ActiveRecord โดยปกติคุณจะใช้ HABTM เมื่อคุณต้องการเชื่อมโยงสองโมเดลด้วยความสัมพันธ์แบบกลุ่มต่อกลุ่ม

คุณใช้ไฟล์ has_many :throughความสัมพันธ์เมื่อคุณต้องการโต้ตอบกับตารางการเข้าร่วมเป็นโมเดล Rails พร้อมด้วยคีย์หลักและความสามารถในการเพิ่มคอลัมน์ที่กำหนดเองลงในข้อมูลที่รวมเข้าด้วยกัน ข้อมูลหลังนี้มีความสำคัญอย่างยิ่งสำหรับข้อมูลที่เกี่ยวข้องกับแถวที่รวมเข้าด้วยกัน แต่ไม่ได้เป็นของโมเดลที่เกี่ยวข้องจริงๆตัวอย่างเช่นการจัดเก็บค่าที่คำนวณได้จากฟิลด์ในแถวที่รวม

ดูสิ่งนี้ด้วย

ในคู่มือการเชื่อมโยง Active Recordคำแนะนำจะอ่าน:

หลักการง่ายๆที่สุดคือคุณควรตั้งค่า has_many: ผ่านความสัมพันธ์หากคุณต้องการทำงานกับรูปแบบความสัมพันธ์เป็นเอนทิตีอิสระ หากคุณไม่จำเป็นต้องทำอะไรกับรูปแบบความสัมพันธ์การตั้งค่าความสัมพันธ์ has_and_belongs_to_many อาจจะง่ายกว่า (แม้ว่าคุณจะต้องจำไว้ว่าต้องสร้างตารางการเข้าร่วมในฐานข้อมูล)

คุณควรใช้ has_many: ถึงแม้ว่าคุณต้องการการตรวจสอบความถูกต้องการเรียกกลับหรือแอตทริบิวต์เพิ่มเติมในโมเดลการเข้าร่วม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.