เป็นของเพื่อผ่านการเชื่อมโยง


141

ด้วยการเชื่อมโยงดังต่อไปนี้ฉันต้องอ้างอิงสิ่งQuestionที่Choiceแนบมาผ่านจากChoiceโมเดล ฉันพยายามใช้belongs_to :question, through: :answerเพื่อดำเนินการนี้

class User
  has_many :questions
  has_many :choices
end

class Question
  belongs_to :user
  has_many :answers
  has_one :choice, :through => :answer
end

class Answer
  belongs_to :question
end

class Choice
  belongs_to :user
  belongs_to :answer
  belongs_to :question, :through => :answer

  validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ]
end

ฉันได้รับ

NameError ค่าคงที่ที่ไม่ได้กำหนดค่าเริ่มต้น User::Choice

เมื่อฉันพยายามทำ current_user.choices

มันใช้งานได้ดีถ้าฉันไม่ได้รวม

belongs_to :question, :through => :answer

แต่ฉันต้องการที่จะใช้เพราะฉันต้องการที่จะสามารถทำ validates_uniqueness_of

ฉันอาจจะมองเห็นบางสิ่งที่เรียบง่าย ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชม


1
อาจจะคุ้มค่าที่จะเปลี่ยนคำตอบที่ได้รับการยอมรับให้เป็นตัวแทน
23inhouse

คำตอบ:


60

การbelongs_toเชื่อมโยงไม่สามารถมี:throughตัวเลือก คุณดีแคชquestion_idบนChoiceและการเพิ่มดัชนีที่ไม่ซ้ำกับตาราง (โดยเฉพาะอย่างยิ่งเพราะvalidates_uniqueness_ofมีแนวโน้มที่จะเงื่อนไขการแข่งขัน)

หากคุณหวาดระแวงให้เพิ่มการตรวจสอบที่กำหนดเองเพื่อChoiceยืนยันว่าคำตอบนั้นquestion_idตรงกัน แต่ดูเหมือนว่าผู้ใช้ปลายทางไม่ควรได้รับโอกาสในการส่งข้อมูลที่จะสร้างความไม่ตรงกันประเภทนี้


ขอบคุณสตีเฟ่นฉันไม่ต้องการที่จะเชื่อมโยงโดยตรงกับ question_id แต่ฉันคิดว่ามันเป็นวิธีที่ง่ายที่สุด ความคิดดั้งเดิมของฉันคือเนื่องจาก "คำตอบ" เป็นของ "คำถาม" ฉันสามารถผ่าน "คำตอบ" เพื่อไปที่ "คำถาม" ได้เสมอ แต่คุณคิดว่านั่นไม่ใช่เรื่องง่ายที่จะทำหรือคุณคิดว่านั่นเป็นเพียงสคีมาที่ไม่ดีหรือไม่?
vinhboy

หากคุณต้องการข้อ จำกัด / การตรวจสอบความถูกต้องที่ไม่ซ้ำกันฟิลด์ที่กำหนดขอบเขตจะต้องมีอยู่ในตารางเดียวกัน จำไว้ว่ามีเงื่อนไขการแข่งขัน
stephencelis

1
>> ดูเหมือนว่าผู้ใช้ปลายทางไม่ควรได้รับโอกาสในการส่งข้อมูลที่จะสร้างความไม่ตรงกันแบบนี้ - คุณไม่สามารถรับประกันได้ว่าผู้ใช้ "ไม่มีโอกาสทำอะไร" เว้นแต่คุณจะทำการตรวจสอบด้านเซิร์ฟเวอร์อย่างชัดเจน
Konstantin

376

คุณยังสามารถมอบหมาย:

class Company < ActiveRecord::Base
  has_many :employees
  has_many :dogs, :through => :employees
end

class Employee < ActiveRescord::Base
  belongs_to :company
  has_many :dogs
end

class Dog < ActiveRecord::Base
  belongs_to :employee

  delegate :company, :to => :employee, :allow_nil => true
end

27
+1 นี่เป็นวิธีที่สะอาดที่สุดในการทำเช่นนี้ (อย่างน้อยฉันก็คิดได้)
ออร์แลนโด

9
มีวิธีในการทำเช่นนี้กับ JOIN เพื่อที่จะไม่ใช้ข้อความค้นหามากมาย?
Tallboy

1
ฉันอยากรู้จักตัวเอง ทุกอย่างที่ฉันพยายามไล่ออก 3 ตัวเลือก คุณสามารถระบุแลมบ์ดา "-> {joins: something}" ในการเชื่อมโยง การเข้าร่วมถูกไล่ออก แต่ต่อมาจะมีการเลือกแบบอื่น ฉันไม่สามารถปรับมันได้
Renra

2
@Tallboy คิวรีแบบเลือกที่มีการจัดทำดัชนีอย่างสมบูรณ์บนคีย์หลักนั้นมักจะดีกว่าแบบสอบถาม JOIN เดียว เข้าร่วมทำให้ฐานข้อมูลทำงานหนัก
Ryan McGeary

1
allow_nil ทำอะไร พนักงานไม่ควรมี บริษัท เสมอหรือ
aaron-coding

115

เพียงใช้has_oneแทนของbelongs_toคุณ:throughเช่นนี้

class Choice
  belongs_to :user
  belongs_to :answer
  has_one :question, :through => :answer
end

ไม่เกี่ยวข้อง แต่ฉันลังเลที่จะใช้ validates_uniqueness_of แทนที่จะใช้ข้อ จำกัด ที่ไม่ซ้ำกันในฐานข้อมูลของคุณ เมื่อคุณทำเช่นนี้ในทับทิมคุณมีเงื่อนไขการแข่งขัน


38
คำเตือนครั้งใหญ่ด้วยโซลูชันนี้ เมื่อใดก็ตามที่คุณบันทึกตัวเลือกมันจะบันทึกคำถามเสมอเว้นแต่autosave: falseจะมีการตั้ง
Chris Nicola

@ChrisNicola คุณช่วยอธิบายความหมายของคุณได้ฉันไม่เข้าใจว่าคุณหมายถึงอะไร
aks

สิ่งที่ฉันหมายถึงที่ไหน หากคุณหมายถึงข้อ จำกัด ที่ไม่ซ้ำใครฉันหมายถึงการเพิ่มดัชนี UNIQUE ลงในคอลัมน์ / ฟิลด์ที่ต้องไม่ซ้ำกันในฐานข้อมูล
Chris Nicola

4

แนวทางของฉันคือการสร้างแอตทริบิวต์เสมือนแทนที่จะเพิ่มคอลัมน์ฐานข้อมูล

class Choice
  belongs_to :user
  belongs_to :answer

  # ------- Helpers -------
  def question
    answer.question
  end

  # extra sugar
  def question_id
    answer.question_id
  end
end

วิธีนี้ค่อนข้างง่าย แต่มาพร้อมกับการแลกเปลี่ยน มันต้องมีทางรถไฟที่จะโหลดจากฐานข้อมูลแล้วanswer questionสิ่งนี้สามารถปรับให้เหมาะสมในภายหลังได้โดยการโหลดการเชื่อมโยงที่คุณต้องการ (เช่นc = Choice.first(include: {answer: :question})) อย่างไรก็ตามหากจำเป็นต้องเพิ่มประสิทธิภาพนี้คำตอบของ stephencelis อาจเป็นการตัดสินใจที่ดีกว่า

มีเวลาและสถานที่สำหรับตัวเลือกบางอย่างและฉันคิดว่าตัวเลือกนี้ดีกว่าเมื่อสร้างต้นแบบ ฉันจะไม่ใช้มันสำหรับรหัสการผลิตเว้นแต่ฉันจะรู้ว่ามันเป็นกรณีการใช้งานไม่บ่อยนัก


1

ดูเหมือนว่าสิ่งที่คุณต้องการคือผู้ใช้ที่มีคำถามมากมาย
คำถามมีคำตอบมากมายซึ่งหนึ่งในนั้นคือทางเลือกของผู้ใช้

นี่คือสิ่งที่คุณเป็นหรือไม่

ฉันจะสร้างแบบจำลองเช่นนั้นตามบรรทัดเหล่านี้:

class User
  has_many :questions
end

class Question
  belongs_to :user
  has_many   :answers
  has_one    :choice, :class_name => "Answer"

  validates_inclusion_of :choice, :in => lambda { answers }
end

class Answer
  belongs_to :question
end

1

ดังนั้นคุณไม่สามารถมีพฤติกรรมที่คุณต้องการ แต่คุณสามารถทำสิ่งที่รู้สึกเช่นนั้นได้ คุณต้องการที่จะสามารถทำChoice.first.question

สิ่งที่ฉันทำในอดีตคืออะไรแบบนี้

class Choice
  belongs_to :user
  belongs_to :answer
  validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ]
  ...
  def question
    answer.question
  end
end

ด้วยวิธีนี้คุณสามารถโทรถามคำถามได้ที่ตัวเลือก


-1

has_many :choicesสร้างการเชื่อมโยงชื่อไม่ได้choices choiceลองใช้current_user.choicesแทน

ดูเอกสารประกอบActiveRecord :: Associationsสำหรับข้อมูลเกี่ยวกับhas_manyเวทมนตร์


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