วิธีเพิ่มเรกคอร์ดใน has_many: ผ่านการเชื่อมโยงในราง


97
class Agents << ActiveRecord::Base
  belongs_to :customer
  belongs_to :house
end

class Customer << ActiveRecord::Base
  has_many :agents
  has_many :houses, through: :agents
end

class House << ActiveRecord::Base
  has_many :agents
  has_many :customers, through: :agents
end

ฉันจะเพิ่มAgentsรุ่นสำหรับCustomer?

วิธีนี้เป็นวิธีที่ดีที่สุดหรือไม่?

Customer.find(1).agents.create(customer_id: 1, house_id: 1)

ข้างต้นใช้งานได้ดีจากคอนโซลอย่างไรก็ตามฉันไม่รู้ว่าจะบรรลุสิ่งนี้ในแอปพลิเคชันจริงได้อย่างไร

ลองนึกภาพว่ามีการกรอกแบบฟอร์มสำหรับลูกค้าที่ใช้house_idเป็นข้อมูลป้อนข้อมูล จากนั้นฉันจะทำสิ่งต่อไปนี้ในคอนโทรลเลอร์ของฉันหรือไม่?

def create 
  @customer = Customer.new(params[:customer])
  @customer.agents.create(customer_id: @customer.id, house_id: params[:house_id])
  @customer.save
end

โดยรวมแล้วฉันสับสนว่าจะเพิ่มระเบียนในhas_many :throughตารางได้อย่างไร?


คุณจะจัดเก็บฟังก์ชัน "create" ไว้ในคอนโทรลเลอร์ใด
blkpingu

คำตอบ:


166

ฉันคิดว่าคุณสามารถทำได้ง่ายๆ:

 @cust = Customer.new(params[:customer])
 @cust.houses << House.find(params[:house_id])

หรือเมื่อสร้างบ้านหลังใหม่ให้กับลูกค้า:

 @cust = Customer.new(params[:customer])
 @cust.houses.create(params[:house])

คุณยังสามารถเพิ่มผ่านรหัส:

@cust.house_ids << House.find(params[:house_id])

16
FYI: คุณไม่สามารถสร้างบ้านที่เกี่ยวข้องได้เว้นแต่ผู้ปกครองจะได้รับการบันทึกไว้แล้ว
Ricardo Otero

นั่นจะต้องเป็นวิธีแก้ปัญหาที่ดีที่สุดสำหรับปัญหานี้ที่ฉันเจอ +1 สำหรับคุณ
Daniel Bonnell

@RicardoOtero ฉันเดาว่าเราสามารถใช้buildistead ของcreate?
Karan

@Mischa ฉันจะจัดการกับข้อผิดพลาดได้อย่างไรหาก House.find (params [: house_id]) ไม่มี .. ฉันพบข้อผิดพลาดของ TypeMismatch หาก params [: house_id] เป็นศูนย์ .. ฉันใช้การช่วยเหลืออยู่แล้ว แต่จะมีทางไหนดีกว่ากัน .. ??
Vishal

1
ฉันสังเกตเห็นว่าการใช้<<ตัวดำเนินการจะแทรกสองครั้งในบางกรณี ดังนั้นcreateวิธีการคือวิธีที่ดีที่สุด
Swaps

79

'วิธีที่ดีที่สุด' ขึ้นอยู่กับความต้องการของคุณและสิ่งที่รู้สึกสบายใจที่สุด ความสับสนมาจากความแตกต่างของพฤติกรรมnewและcreateวิธีการของ ActiveRecord และตัว<<ดำเนินการ

newวิธี

newจะไม่เพิ่มบันทึกการเชื่อมโยงให้คุณ คุณต้องสร้างHouseและAgentบันทึกด้วยตัวคุณเอง:

house = @cust.houses.new(params[:house])
house.save
agent = Agent(customer_id: @cust.id, house_id: house.id)
agent.save

โปรดทราบว่า@cust.houses.newและHouse.newมีประสิทธิภาพเหมือนกันเนื่องจากคุณต้องสร้างAgentเรกคอร์ดในทั้งสองกรณี

<<ผู้ประกอบการ

ตามที่ Mischa กล่าวถึงคุณยังสามารถใช้ตัว<<ดำเนินการในคอลเล็กชันได้ สิ่งนี้จะสร้างAgentโมเดลให้คุณเท่านั้นคุณต้องสร้างHouseโมเดล:

house = House.create(params[:house])
@cust.houses << house
agent = @cust.houses.find(house.id)

createวิธี

createจะสร้างทั้งสองHouseและAgentบันทึกให้คุณ แต่คุณจะต้องค้นหาAgentโมเดลหากคุณตั้งใจที่จะคืนค่านั้นกลับสู่มุมมองหรือ API ของคุณ:

house = @cust.houses.create(params[:house])
agent = @cust.agents.where(house: house.id).first

หมายเหตุสุดท้ายหากคุณต้องการให้มีการเพิ่มข้อยกเว้นเมื่อสร้างให้houseใช้ตัวดำเนินการปังแทน (เช่นnew!และcreate!)


2
บรรทัดควรagent = @cust.houses.find(house.id)อ่านagent = @cust.agents.find(house.id)แทนหรือไม่? agentตัวแปรใน "วิธีการใหม่" ที่แตกต่างกันไปagentในตัวอย่างหลัง อาจสร้างความสับสนให้กับผู้ที่ทำงานกับแอตทริบิวต์เพิ่มเติมบนโต๊ะเข้าร่วม
vaughan

คุณสามารถอธิบายรายละเอียดเกี่ยวกับการดึงข้อมูลจากตัวแทนตารางร่วมโดยไม่ต้องมีตัวอย่างข้อผิดพลาด N + 1 ที่แสดงบ้านทั้งหมดและตัวแทนที่เกี่ยวข้องสำหรับลูกค้าที่ระบุ
AnkitaP

6

อีกวิธีหนึ่งในการเพิ่มการเชื่อมโยงคือการใช้คอลัมน์ Foreign Key:

agent = Agent.new(...)
agent.house = House.find(...)
agent.customer = Customer.find(...)
agent.save

หรือใช้ชื่อคอลัมน์ที่แน่นอนส่ง ID ของเร็กคอร์ดที่เกี่ยวข้องแทนเร็กคอร์ด

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