web-dev-qa-db-fra.com

appartient_à travers des associations

Étant donné les associations suivantes, je dois référencer le Question auquel un Choice est attaché via le modèle Choice. J'ai essayé d'utiliser belongs_to :question, through: :answer pour effectuer cette action.

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

Je reçois

NameError constante non initialisée User::Choice

quand j'essaie de faire current_user.choices

Cela fonctionne bien, si je n'inclus pas le

belongs_to :question, :through => :answer

Mais je veux utiliser cela parce que je veux pouvoir faire le validates_uniqueness_of

Je néglige probablement quelque chose de simple. Toute aide serait appréciée.

128
vinhboy

UNE belongs_to l'association ne peut pas avoir un :through option. Vous feriez mieux de mettre en cache le question_id sur Choice et l’ajout d’un index unique à la table (surtout parce que validates_uniqueness_of est sujet aux conditions de compétition).

Si vous êtes paranoïaque, ajoutez une validation personnalisée à Choice qui confirme que la réponse est question_id correspond, mais il semble que l'utilisateur final ne devrait jamais avoir la possibilité de soumettre des données qui créeraient ce type d'incohérence.

58
stephencelis

Vous pouvez également déléguer:

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

Il suffit d'utiliser has_one au lieu de belongs_to dans votre :though, comme ça:

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

Sans lien, mais j'hésiterais à utiliser validates_uniqueness_of au lieu d'utiliser une contrainte unique appropriée dans votre base de données. Lorsque vous faites cela dans Ruby vous avez des conditions de course.

99
mrm

Mon approche consistait à créer un attribut virtuel au lieu d’ajouter des colonnes de base de données.

class Choice
  belongs_to :user
  belongs_to :answer

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

  # extra sugar
  def question_id
    answer.question_id
  end
end

Cette approche est assez simple, mais s'accompagne de compromis. Il faut Rails pour charger answer à partir de la base de données, puis question. Ceci peut être optimisé ultérieurement en chargeant avec impatience les associations dont vous avez besoin (c'est-à-dire c = Choice.first(include: {answer: :question})), cependant, si cette optimisation est nécessaire, la réponse de stephencelis est probablement une meilleure décision de performance.

Il y a un moment et un lieu pour certains choix, et je pense que ce choix est meilleur pour le prototypage. Je ne l'utiliserais pas pour le code de production, sauf si je savais que c'était pour un cas d'utilisation peu fréquent.

3
Eric Hu

Donc, vous ne pouvez pas avoir le comportement que vous voulez, mais vous pouvez faire quelque chose qui vous ressemble. Vous voulez pouvoir faire Choice.first.question

ce que j'ai fait dans le passé est quelque chose comme ça

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

de cette façon, vous pouvez maintenant appeler la question sur Choice

1
MZaragoza

Cela ressemble à ce que vous voulez, c'est un utilisateur qui a beaucoup de questions.
La question a de nombreuses réponses, dont le choix de l'utilisateur.

Est-ce ce que vous recherchez?

Je modéliserais quelque chose comme ça dans ce sens:

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