2010-10-26 24 views
104

Với các liên kết sau, tôi cần tham khảo Question rằng Choice được đính kèm thông qua mô hình Choice. Tôi đã cố gắng sử dụng belongs_to :question, through: :answer để thực hiện tác vụ này.thuộc về các hiệp hội

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 nhận

NameError uninitialized liên tục 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 điều đó vì tôi muốn có thể thực hiện validates_uniqueness_of

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

+0

Có lẽ nó có giá trị thay đổi chấp nhận câu trả lời cho người được ủy quyền? – 23inhouse

Trả lời

47

Hiệp hội belongs_to không thể có tùy chọn :through. Bạn nên tắt bộ nhớ đệm question_id trên Choice và thêm chỉ mục duy nhất vào bảng (đặc biệt là vì validates_uniqueness_of dễ bị điều kiện chủng tộc).

Nếu bạn hoang tưởng, hãy thêm xác thực tùy chỉnh vào Choice xác nhận rằng kết quả phù hợp là question_id, nhưng có vẻ như người dùng cuối không bao giờ có cơ hội gửi dữ liệu sẽ tạo loại không phù hợp này.

+0

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" để đến "câu hỏi". Nhưng bạn có nghĩ rằng đó không phải là dễ dàng để làm, hoặc bạn có nghĩ rằng đó chỉ là một lược đồ xấu? – vinhboy

+0

Nếu bạn muốn có một ràng buộc/xác thực duy nhất, các trường có 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

has_many :choices tạo liên kết có tên choices, không phải choice. Hãy thử sử dụng current_user.choices để thay thế.

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

+1

Cảm ơn sự giúp đỡ của bạn Michael, tuy nhiên, đó là một lỗi đánh máy về phía tôi. Tôi đã làm current_user.choices. Lỗi này có liên quan đến việc tôi muốn gán thuộc tính cho người dùng và câu hỏi. – vinhboy

1

Có vẻ như những gì bạn muốn là 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 dõi không?

tôi sẽ mô hình một cái gì đó như thế cùng 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 
4

cách tiếp cận của tôi là để thực hiện 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ự cân bằng. Nó yêu cầu Rails tải answer từ 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 (tức là 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 các lựa chọn nhất định và tôi nghĩ lựa chọn này là 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 nó là cho một trường hợp sử dụng không thường xuyên.

292

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

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 
+14

+1, đây là cách sạch nhất để thực hiện việc này. (ít nhất là tôi có thể nghĩ) – Orlando

+5

+1, http://www.simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate/ – shweta

+5

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

75

Chỉ cần sử dụng has_one thay vì belongs_to trong :though của bạn, 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 muốn được do dự sử dụng validates_uniqueness_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.

+27

Cảnh báo lớn với giải pháp này. Bất cứ khi nào bạn lưu chọn, nó sẽ luôn luôn lưu câu hỏi trừ khi 'autosave: false' được thiết lập. –

+0

@ChrisNicola bạn có thể giải thích ý bạn là gì, tôi không hiểu ý bạn là gì. – aks

+0

Ý tôi là ở đâu? 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. –

15

Bạn chỉ có thể sử dụng has_one ở vị trí của belongs_to:

has_one :question, :through => :answer 
+10

Câu trả lời này có vẻ rất giống với http://stackoverflow.com/a/15649020/38765 –

+0

Đồng ý với @AndrewGrimm. Câu trả lời này giống với câu trả lời từ mrm, ngoại trừ một năm rưỡi sau đó. – jeffdill2

+1

mmmmmmmmmmrmrmrmrmmr cách đáng ngờ – zeion

0

Vì vậy, bạn không thể có những hành vi mà bạn muốn nhưng bạn có thể làm điều gì đó mà cảm thấy như nó. Bạn muốn có thể làm Choice.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 

cách này, bây giờ bạn có thể gọi câu hỏi về Choice

Các vấn đề liên quan