Rails: การใช้บิลด์กับการเชื่อมโยง has_one ในราง


143

ในตัวอย่างนี้ฉันสร้าง a userโดยไม่มีprofileจากนั้นสร้าง a profileสำหรับผู้ใช้นั้นในภายหลัง ฉันลองใช้บิลด์กับhas_oneสมาคม แต่มันระเบิด has_manyวิธีเดียวที่ผมเห็นการทำงานนี้คือการใช้ ควรจะมีเพียงหนึ่งมากที่สุดuserprofile

ฉันได้ลองสิ่งนี้แล้ว ฉันมี:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

แต่เมื่อฉัน:

user.build_profile 

ฉันได้รับข้อผิดพลาด:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

มีวิธีในทางรถไฟที่จะมีการเชื่อมโยง 0 หรือ 1 หรือไม่?


คุณลองทำอะไรกันแน่ คุณช่วยกรุณาโพสต์รหัสได้ไหม
Ju Nogueira

คำตอบ:


359

buildลายเซ็นวิธีที่แตกต่างกันสำหรับการ has_oneและhas_manyสมาคม

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

ไวยากรณ์ของบิลด์สำหรับการhas_manyเชื่อมโยง:

user.messages.build

ไวยากรณ์ของบิลด์สำหรับการhas_oneเชื่อมโยง:

user.build_profile  # this will work

user.profile.build  # this will throw error

อ่านเอกสารการhas_oneเชื่อมโยงสำหรับรายละเอียดเพิ่มเติม


28
ไวยากรณ์ที่แตกต่างกันสำหรับ has_one มักจะจับฉันออกมา ... dammit!
Galaxy

11
มันตลกที่คำตอบยอดนิยมและได้รับการยอมรับที่นี่ตอบคำถามที่แตกต่างจากที่ OP ถาม
Ajedi32

คาดคะเนหากผู้ใช้เป็นรายละเอียด (ตารางผู้ใช้มีความหมายมี PROFILE_ID foreign_key ในตารางของมัน) แล้วยังสร้างรายละเอียดสำหรับผู้ใช้จะทำงานดังกล่าวข้างต้นเช่น แต่สำหรับการดำเนินการใหม่เท่านั้นuser.build_profile สำหรับการแก้ไขuser.build_profile if user.profile.nil? และถ้าคุณต้องการที่จะสร้างรายขณะที่การสร้างผู้ใช้แล้วเขียนaccepts_nested_attributes_for :profileในเรื่องนี้ รูปแบบของผู้ใช้ และในรูปแบบที่ผู้ใช้จะถูกสร้างเขียน<%= f.simple_fields_for :profile do |p| %>นี้และไป
ความกระตือรือร้น

แต่ทำไมพฤติกรรมที่แตกต่างนี้ถูกเก็บไว้สำหรับ has_one หรือ has_many มีเหตุผลบางอย่างในขณะออกแบบฉันคิดและคาดหวัง
อยากรู้อยากเห็น

@ Ajedi32 คำตอบที่ตรงกับชื่อของคำถาม แต่ไม่ใช่เนื้อหา ระบุว่า ( build_<association>) เป็นพฤติกรรมที่แปลกและคาดไม่ถึงใน Rails มีคนมากมายที่มองหาคำตอบนี้มากกว่าคำตอบของคำถามจริงถ้าคุณรู้ว่าฉันหมายถึงอะไร
Max Williams

19

ลองดูข้อความแสดงข้อผิดพลาดดู มันจะบอกคุณว่าคุณไม่จำเป็นต้องมีคอลัมน์user_idในตารางราย การตั้งค่าความสัมพันธ์ในแบบจำลองเป็นเพียงส่วนหนึ่งของคำตอบ

คุณต้องสร้างการย้ายข้อมูลที่เพิ่มuser_idคอลัมน์ในตารางโปรไฟล์ Rails คาดว่าสิ่งนี้จะอยู่ที่นั่นและหากไม่ใช่คุณจะไม่สามารถเข้าถึงโปรไฟล์ได้

สำหรับข้อมูลเพิ่มเติมโปรดดูที่ลิงค์นี้:

พื้นฐานความสัมพันธ์


1
ฉันเพิ่งพบปัญหาของฉัน หนังสือที่ฉันเรียนรู้ไม่ได้อธิบายการสร้างคีย์ต่างประเทศเป็นอย่างดี ฉันสร้างการย้ายข้อมูลใหม่ซึ่งเพิ่มคีย์ต่างประเทศให้กับแบบจำลองของฉัน ขอบคุณ
espinet

คุณต้องการสร้างคอลัมน์ด้วยตัวเองทุกครั้งหรือไม่? ฉันมีความคิดนี้ว่ามันเกิดขึ้นโดยอัตโนมัติ ฉันไม่รู้ว่าฉันมีความคิดนั้นอยู่ที่ไหน
Rimian

rails g model profile user:references:index address:string bio:textคุณสามารถเพิ่มคอลัมน์เมื่อสร้างรูปแบบที่ใช้บรรทัดคำสั่งบางอย่างเช่น
duykhoa

1

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

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

ตอนนี้การเรียก#profileเมธอดจะส่งคืนโปรไฟล์ที่เชื่อมโยงหรือสร้างอินสแตนซ์ใหม่

แหล่งที่มา: เมื่อลิงปะวิธีคุณสามารถเรียกวิธีการแทนที่จากการใช้งานใหม่ได้หรือไม่


1
ในรางปัจจุบัน (ทดสอบบน 6.0.2.2) def profile; super || build_profile; endคุณสามารถลดความซับซ้อนนี้ไปที่:
glasz

-14

has_oneมันควรจะเป็น หากbuildไม่ทำงานคุณสามารถใช้new:

ModelName.new( :owner => @owner )

เป็นเช่นเดียวกับ

@owner.model_names.build

11
สิ่งนี้ไม่เหมือนกัน: ถ้าคุณสร้าง model_name ใหม่พร้อมบิลด์เมื่อ @owner ถูกบันทึกแล้ว model_name ใหม่จะถูกบันทึกด้วย ดังนั้นคุณสามารถใช้บิลด์เพื่อสร้างพาเรนต์และลูกซึ่งจะได้รับการบันทึกด้วยกัน นี่ไม่ใช่กรณีถ้าคุณสร้าง model_name ด้วย. new
Max Williams
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.