thuộc về thông qua các hiệp hội


141

Đưa ra các hiệp hội sau đây, tôi cần tham chiếu cái Questionmà a Choiceđược đính kèm từ Choicemô hình. Tôi đã cố gắng sử dụng belongs_to :question, through: :answerđể thực hiện hành động này.

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

Tôi đang nhận được

NameError hằng số chưa được khởi tạo User::Choice

khi tôi cố gắng làm current_user.choices

Nó hoạt động tốt, nếu tôi không bao gồm

belongs_to :question, :through => :answer

Nhưng tôi muốn sử dụng nó bởi vì tôi muốn có thể làm validates_uniqueness_of

Tôi có lẽ đang nhìn một cái gì đó đơn giản. Bất kỳ trợ giúp sẽ được đánh giá cao.


1
Có lẽ nên thay đổi câu trả lời được chấp nhận cho người đại biểu?
23

Câu trả lời:


60

Một belongs_tohiệp hội không thể có một :throughlựa chọn. Bạn nên bộ nhớ đệm question_idtrên Choicevà thêm một chỉ số duy nhất để bàn (đặc biệt là bởi vì validates_uniqueness_oflà dễ bị điều kiện chủng tộc).

Nếu bạn bị hoang tưởng, hãy thêm xác thực tùy chỉnh để Choicexác nhận rằng câu trả lời question_idtrùng khớp, nhưng có vẻ như người dùng cuối sẽ không bao giờ có cơ hội gửi dữ liệu sẽ tạo ra loại không khớp này.


Cảm ơn Stephen, tôi thực sự không muốn phải liên kết trực tiếp với question_id, nhưng tôi đoán đó là cách dễ nhất. Suy nghĩ ban đầu của tôi là, vì "câu trả lời" thuộc về "câu hỏi", tôi luôn có thể đi qua "câu trả lời" để đi đến "câu hỏi". Nhưng bạn có nghĩ rằng điều đó không dễ thực hiện hay bạn nghĩ đó chỉ là một lược đồ tồi?
vinhboy

Nếu bạn muốn một ràng buộc / xác nhận duy nhất, các trường trong phạm vi phải tồn tại trong cùng một bảng. Hãy nhớ rằng, có điều kiện chủng tộc.
stephencelis

1
>> có vẻ như người dùng cuối sẽ không bao giờ có cơ hội gửi dữ liệu sẽ tạo ra loại không khớp này. - Bạn không bao giờ có thể đảm bảo rằng người dùng "không có cơ hội để làm điều gì đó" trừ khi bạn thực hiện kiểm tra phía máy chủ rõ ràng cho việc đó.
Konstantin

376

Bạn cũng có thể ủy thác:

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, đây là cách sạch nhất để làm điều này. (ít nhất là tôi có thể nghĩ)
Orlando

9
Có cách nào để làm điều này với THAM GIA để nó không sử dụng quá nhiều truy vấn không?
Tallboy

1
Tôi muốn biết bản thân mình. Tất cả mọi thứ tôi đã cố gắng sa thải 3 lựa chọn. Bạn có thể chỉ định lambda "-> {tham gia: một cái gì đó}" trên một liên kết. Tham gia được kích hoạt nhưng sau đó chọn khác. Tôi đã không thể điều chỉnh này.
Renra

2
@Tallboy Một vài truy vấn chọn được lập chỉ mục hoàn hảo trên các khóa chính hầu như luôn luôn tốt hơn bất kỳ truy vấn THAM GIA nào. Tham gia làm cho cơ sở dữ liệu làm việc chăm chỉ.
Ryan McGeary

1
Perm_nil làm gì? Không phải nhân viên luôn có một công ty sao?
mã hóa aaron

115

Chỉ cần sử dụng has_onethay vì belongs_totrong của bạn :through, như thế này:

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

Không liên quan, nhưng tôi sẽ do dự khi sử dụng validates_uniquety_of thay vì sử dụng một ràng buộc duy nhất thích hợp trong cơ sở dữ liệu của bạn. Khi bạn làm điều này trong ruby, bạn có điều kiện chủng tộc.


38
Cảnh báo lớn với giải pháp này. Bất cứ khi nào bạn lưu Lựa chọn, nó sẽ luôn lưu Câu hỏi trừ khi autosave: falseđược đặt.
Chris Nicola

@ChrisNicola bạn có thể vui lòng giải thích ý của bạn không, tôi không hiểu ý của bạn.
aks

Ý tôi là gì? Nếu bạn có nghĩa là một ràng buộc duy nhất thích hợp, tôi có nghĩa là thêm một chỉ mục UNIQUE vào cột / trường phải là duy nhất trong cơ sở dữ liệu.
Chris Nicola

4

Cách tiếp cận của tôi là tạo một thuộc tính ảo thay vì thêm các cột cơ sở dữ liệu.

class Choice
  belongs_to :user
  belongs_to :answer

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

  # extra sugar
  def question_id
    answer.question_id
  end
end

Cách tiếp cận này khá đơn giản, nhưng đi kèm với sự đánh đổi. Nó yêu cầu Rails tải answertừ db và sau đó question. Điều này có thể được tối ưu hóa sau này bằng cách háo hức tải các liên kết bạn cần (ví dụ c = Choice.first(include: {answer: :question})), tuy nhiên, nếu tối ưu hóa này là cần thiết, thì câu trả lời của stephencelis có lẽ là một quyết định hiệu suất tốt hơn.

Có thời gian và địa điểm cho một số lựa chọn nhất định và tôi nghĩ rằng lựa chọn này tốt hơn khi tạo mẫu. Tôi sẽ không sử dụng nó cho mã sản xuất trừ khi tôi biết đó là trường hợp sử dụng không thường xuyên.


1

Có vẻ như những gì bạn muốn là một Người dùng có nhiều Câu hỏi.
Câu hỏi có nhiều Câu trả lời, một trong số đó là Lựa chọn của Người dùng.

Đây có phải là những gì bạn đang theo đuổi?

Tôi sẽ mô hình một cái gì đó như thế dọc theo những dòng này:

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

Vì vậy, bạn không thể có hành vi mà bạn muốn nhưng bạn có thể làm điều gì đó cảm thấy như vậy. Bạn muốn có thể làmChoice.first.question

những gì tôi đã làm trong quá khứ là một cái gì đó như thế này

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

bằng cách này, bây giờ bạn có thể gọi câu hỏi trên Lựa chọn


-1

Việc has_many :choicestạo ra một hiệp hội có tên choices, không choice. Hãy thử sử dụng current_user.choicesthay thế.

Xem tài liệu ActiveRecord :: Associations để biết thông tin về has_manyphép thuật.


1
Cảm ơn sự giúp đỡ của bạn Michael, tuy nhiên, đó là một lỗi đánh máy từ phía tôi. Tôi đã thực hiện current_user.choices. Lỗi này có liên quan đến tôi muốn gán thuộc tính cho người dùng và câu hỏi.
vinhboy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.