Принадлежит через ассоциации

Учитывая следующие ассоциации, мне нужно ссылаться на Question, что a 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

A belongs_to ассоциация не может иметь опцию :through. Вам лучше кэшировать question_id на Choice и добавить уникальный индекс в таблицу (особенно потому, что validates_uniqueness_of подвержен условиям гонки).

Если вы параноик, добавьте специальную проверку на Choice, которая подтверждает соответствие ответа question_id, но похоже, что конечный пользователь никогда не должен иметь возможность отправлять данные, которые могли бы создать такой вид несоответствия.

Ответ 2

Вы также можете делегировать:

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

Ответ 3

Просто используйте has_one вместо belongs_to в :though, например:

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

Несвязанный, но я бы не решался использовать validates_uniqueness_of вместо использования правильного уникального ограничения в вашей базе данных. Когда вы делаете это в рубине, у вас есть условия гонки.

Ответ 4

Вы можете просто использовать has_one вместо belongs_to:

has_one :question, :through => :answer

Ответ 5

Мой подход заключался в создании виртуального атрибута вместо добавления столбцов базы данных.

class Choice
  belongs_to :user
  belongs_to :answer

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

  # extra sugar
  def question_id
    answer.question_id
  end
end

Этот подход довольно прост, но поставляется с компромиссами. Для этого требуется Rails для загрузки answer из db, а затем question. Это может быть оптимизировано позже, если вы захотите загрузить нужные вам ассоциации (т.е. c = Choice.first(include: {answer: :question})), однако, если эта оптимизация необходима, то ответ stephencelis, вероятно, является лучшим решением производительности.

Там время и место для определенных выборов, и я думаю, что этот выбор лучше при прототипировании. Я бы не использовал его для производственного кода, если бы не знал, что это было для редкого варианта использования.

Ответ 6

Похоже на то, что вы хотите, это пользователь, у которого много вопросов.
У Вопроса много ответов, одним из которых является Пользовательский выбор.

Это то, что вам нужно?

Я бы смоделировал что-то вроде этого в следующих строках:

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

Ответ 7

Таким образом, у вас не может быть поведения, которое вы хотите, но вы можете сделать что-то, что ему нравится. Вы хотите иметь возможность 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

таким образом вы можете теперь вызывать вопрос на Выбор

Ответ 8

has_many :choices создает ассоциацию с именем choices, а не choice. Вместо этого используйте current_user.choices.

Информацию о магии has_many см. в документации ActiveRecord:: Associations.