Låt oss börja med att fixa till relationerna lite:
class Question < ActiveRecord::Base
has_many :options
has_many :answers
has_many :users, through: :answers
end
Det är inget tekniskt fel med has_many :answers, :through => :options
men eftersom det finns en direkt relation genom answers.question_id
vi behöver inte gå igenom options
tabell för relationen.
Visar antalet
Om vi helt enkelt gjorde:
<td class="optionCell"><%= option.answers.count %></td>
Detta skulle skapa en otäck n+1
fråga för att hämta antalet svar för varje alternativ. Så vad vi vill göra är att skapa en räknarcache
som lagrar en sammanställning på alternativtabellen.
Låt oss börja med att skapa en migrering för att lägga till kolumnen:
rails g migration AddAnswerCounterCacheToOptions answers_count:integer
rake db:migrate
Sedan säger vi till ActiveRecord att uppdatera sammanräkningen när vi skapar associerade poster, detta ser lite konstigt ut eftersom counter_cache: true
deklarationen finns på belongs_to
sidan medan kolumnen är på den andra men det är precis så AR fungerar.
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
Det finns en liten hake här. Eftersom vi kanske redan har poster måste vi se till att de har korrekta räknare. Du kan göra detta från konsolen men i det långa loppet är det en bra idé att skapa en rake-uppgift .
Option.find_each { |option| Option.reset_counters(option.id, :answers) }
Detta kan ta lite tid eftersom det måste dra varje alternativ och uppdatera antalet.
Nu kan vi visa sammanräkningen så här:
<% 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
är smart nog att använda vår räknarcache-kolumn, men kommer att falla tillbaka till att fråga efter räkningen, vilket är bra för tester.