Приложение в стиле опроса Rails — Показать все ответы по опции

Я новичок в ruby ​​on rails и работаю над своим первым углубленным приложением. Он состоит из четырех таблиц: Вопросы, Параметры, Ответы и Пользователи. Есть список вопросов, и пользователь может проголосовать за уникальный вариант (хранящийся в таблице соединений «Ответы»), я пытаюсь разобраться в ассоциациях таблиц.

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

Вот как я настроил свои отдельные файлы RB:

class Question < ActiveRecord::Base
     has_many :options
     has_many :answers, :through => :options
end

class Option < ActiveRecord::Base
    belongs_to :question
    has_many :answers
end

class Answer < ActiveRecord::Base
    belongs_to :user
    belongs_to :question
    belongs_to :option
end

class User < ActiveRecord::Base
    has_many :answers
    has_many :questions, :through => :answers 
end

Контроллер моих вопросов настроен так, чтобы включить таблицу параметров:

@questions = Question.includes(:options).all

и тело таблицы в моем файле index.html.erb:

<tbody>
   <% @questions.each do |question| %>
      <tr class="<%= cycle('lineOdd', 'lineEven') %>">
         <td><%= question.question_text %></td>
         <td><%= link_to 'Show', question %></td>
         <td><%= link_to 'Edit', edit_question_path(question) %></td>
         <td><%= link_to 'Destroy', question, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
      <% question.options.each do |option_text| %>
         <tr class="backgroundColor1">
            <td class="optionCell"> <%= option_text.option_text %> </td>
         </tr>
      <% end %>
   <% end %>
</tbody>

В классе «Вопрос» я использовал «has_many :answers, :through => :options» — это правильный способ сделать это и как вывести общее количество голосов в строке таблицы под соответствующим параметром.

Нужно ли добавлять или изменять код контроллера вопросов?

Это мой первый пост, извините, если недостаточно информативно!

Спасибо


person classequalsarthur    schedule 03.03.2016    source источник
comment
Действительно приятно видеть первый вопрос, который хорошо продуман и включает в себя всю необходимую информацию. Молодец.   -  person max    schedule 03.03.2016
comment
Ах, спасибо - в данный момент мне очень нравится Rails, так что, надеюсь, я останусь здесь!   -  person classequalsarthur    schedule 03.03.2016


Ответы (1)


Давайте начнем с того, что немного исправим отношения:

class Question < ActiveRecord::Base
  has_many :options
  has_many :answers
  has_many :users, through: :answers
end

Технически в has_many :answers, :through => :options нет ничего неправильного, но поскольку существует прямая связь через answers.question_id, нам не нужно просматривать таблицу options для связи.

Отображение количества

Если бы мы просто сделали:

<td class="optionCell"><%= option.answers.count %></td>

Это создаст неприятный n+1 запрос для получения количества ответов для каждого варианта. Итак, что мы хотим сделать, так это создать кеш-счетчик, в котором будет храниться количество таблица вариантов.

Давайте начнем с создания миграции для добавления столбца:

rails g migration AddAnswerCounterCacheToOptions answers_count:integer
rake db:migrate

Затем мы сообщаем ActiveRecord об обновлении подсчета при создании связанных записей, это выглядит немного странно, поскольку объявление counter_cache: true находится на стороне belongs_to, а столбец — на другой, но именно так работает AR.

class Option < ActiveRecord::Base
  belongs_to :question
  has_many :answers
end

class Answer < ActiveRecord::Base
  belongs_to :user
  belongs_to :question
  belongs_to :option, counter_cache: true
end

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

Option.find_each { |option| Option.reset_counters(option.id, :answers) }

Это может занять некоторое время, так как необходимо получить каждую опцию и обновить счетчик.

Теперь мы можем отобразить подсчет следующим образом:

<% question.options.each do |option| %>
  <tr class="backgroundColor1">
    <td class="optionCell"><%= option.option_text %></td>
    <td class="optionCell"><%= option.answers.size %></td>
  </tr>
<% end %>

.size достаточно умен, чтобы использовать наш столбец кеша счетчика, но вернется к запросу счетчика, что хорошо для тестов.

person max    schedule 03.03.2016
comment
Альтернативой counter_cache является написание предложения select, которое извлекает количество в подзапросе. На самом деле не рекомендуется для новичков, так как вам действительно нужно знать, как ActiveRecord строит запросы и загружает отношения. - person max; 03.03.2016
comment
Большое спасибо, действительно полезно! :-) - person classequalsarthur; 03.03.2016