належить до асоціацій


141

З огляду на наступні асоціації, мені потрібно посилатися на Question те, що а 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
Можливо, варто змінити прийняту відповідь на делегата?
23:00

Відповіді:


60

belongs_toАсоціація не може мати :throughопцію. Ви краще кешування question_idна Choiceі додавання унікального індексу до таблиці (особливо тому , щоvalidates_uniqueness_of схильна до умов гонки).

Якщо ви параноїдальні, додайте спеціальну перевірку, Choiceяка підтверджує відповідність question_idвідповіді, але це здається, що кінцевому користувачеві ніколи не слід надавати можливість подавати дані, які створювали б подібне невідповідність.


Дякую Стівен, мені дуже не хотілося спілкуватися безпосередньо з question_id, але я думаю, що це найпростіший спосіб. Моя оригінальна думка полягала в тому, що "відповідь" належить до "питання", я завжди можу пройти "відповідь", щоб дійти до "питання". Але ти вважаєш, що це не просто зробити, чи ти вважаєш це лише поганою схемою?
vinhboy

Якщо ви хочете отримати унікальне обмеження / валідації, поля, що визначаються, повинні існувати в одній таблиці. Пам'ятайте, існують умови перегонів.
stephencelis

1
>> Це здається, що кінцевому користувачеві ніколи не можна надавати можливість подавати дані, які створювали б подібне невідповідність. - Ви ніколи не можете гарантувати, що користувач "не має можливості щось робити", якщо ви не зробите для цього явну перевірку на сервері.
Костянтин

376

Ви також можете делегувати:

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, це найчистіший спосіб зробити це. (принаймні, що я можу подумати)
Орландо

9
Чи можна зробити це за допомогою JOIN, щоб він не використовував так багато запитів?
Талбой

1
Я хотів би знати себе. Все, що я спробував, звільнив 3 селектора. Ви можете вказати лямбда "-> {приєднується: щось}" в асоціації. Об'єднання запускається, але згодом інший вибір все одно. Я не зміг налаштувати це.
Ренра

2
@Tallboy Кілька ідеально індексованих запитів вибору на первинних клавішах майже завжди кращі, ніж будь-який окремий запит JOIN. Приєднання змушує працювати з базою даних.
Райан Макгірі

1
Що робить дозволений_ніл? Чи не повинен працівник завжди мати компанію?
кодування

115

Просто використовуйте has_oneзамість belongs_toсвого :through, наприклад:

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

Непов’язаний, але я б вагався використовувати validates_uniqueness_of замість того, щоб використовувати належне унікальне обмеження у вашій базі даних. Коли ви робите це в рубіні, у вас є умови перегонів.


38
Велике попередження щодо цього рішення. Щоразу, коли ви зберігаєте Choice, він завжди зберігатиме питання, якщо autosave: falseне встановлено.
Кріс Нікола

@ChrisNicola, будь ласка, поясніть, що ви мали на увазі, я не зрозумів, що ви мали на увазі.
акс

Що я мав на увазі куди? Якщо ви маєте на увазі належне унікальне обмеження, я маю на увазі додавання UNIQUE індексу до стовпця / поля, яке повинно бути унікальним у базі даних.
Кріс Нікола

4

Мій підхід полягав у тому, щоб зробити віртуальний атрибут замість додавання стовпців бази даних.

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, ймовірно, є кращим рішенням щодо ефективності.

Є час і місце для певного вибору, і я думаю, що цей вибір кращий при розробці прототипів. Я б не використовував його для виробничого коду, якщо тільки я не знав, що це для випадків нечастого використання.


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

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

has_many :choicesСтворює об'єднання імені choices, а НЕ choice. Спробуйте використовувати current_user.choicesзамість цього.

Див ActiveRecord :: Associations документацію для отримання інформації про про has_manyмагії.


1
Дякую за вашу допомогу, Майкл, проте, друкарська справа з мого боку. Я вже роблю current_user.choices. Ця помилка має щось спільне зі мною, бажаючи призначити користувачеві last_to та питати.
vinhboy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.