web-dev-qa-db-ja.com

関連付けによるbelongs_to

次の関連付けを考えると、QuestionChoiceモデルから接続されている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を実行できるようにしたいので、それを使用したいです。

おそらく、単純なものを見落としているでしょう。任意の助けをいただければ幸いです。

128
vinhboy

belongs_toアソシエーションに:throughオプションを含めることはできません。 question_idChoiceにキャッシュし、テーブルに一意のインデックスを追加することをお勧めします(特にvalidates_uniqueness_ofは競合状態になりやすいため)。

偏執的な場合は、Choiceにカスタム検証を追加して、回答のquestion_idが一致することを確認しますが、この種のデータを作成するデータをエンドユーザーに送信する機会を与えないように聞こえますミスマッチ。

58
stephencelis

以下を委任することもできます。

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
347
Renra

次のように、has_onebelongs_toの代わりに:thoughを使用します。

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

無関係ですが、データベースで適切な一意制約を使用する代わりにvalidates_uniqueness_ofを使用することをためらいます。 Rubyでこれを行うと、競合状態になります。

99
mrm

私のアプローチは、データベース列を追加する代わりに仮想属性を作成することでした。

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をロードするにはRailsが必要で、次にquestionが必要です。これは後で必要なアソシエーション(つまりc = Choice.first(include: {answer: :question}))を積極的にロードすることで最適化できますが、この最適化が必要な場合は、おそらくステファンスリスの答えがパフォーマンスのより良い決定です。

特定の選択肢には時間と場所があり、プロトタイピングの際にはこの選択肢の方が良いと思います。使用頻度の低いユースケースであることがわからない限り、本番コードには使用しません。

3
Eric Hu

だからあなたが望む行動をすることはできませんが、あなたはそれのように感じる何かをすることができます。 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

このようにして、Choiceで質問を呼び出すことができます

1
MZaragoza

あなたが望むのは、多くの質問を持つユーザーであるように思えます。
質問には多くの回答があり、そのうちの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
Adam Tanner