Нека започнем, като коригираме малко отношенията:
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
е достатъчно интелигентен, за да използва нашата колона за кеш на брояча, но ще се върне към запитване за броя, което е добро нещо за тестове.