Comencemos arreglando un poco las relaciones:
class Question < ActiveRecord::Base
has_many :options
has_many :answers
has_many :users, through: :answers
end
No hay nada técnicamente incorrecto con has_many :answers, :through => :options
pero como hay una relación directa a través de answers.question_id
no necesitamos pasar por las options
tabla para la relación.
Mostrando el conteo
Si simplemente hiciéramos:
<td class="optionCell"><%= option.answers.count %></td>
Esto crearía un desagradable n+1
consulta para obtener el recuento de las respuestas para cada opción. Entonces, lo que queremos hacer es crear un contador de caché
que almacena una cuenta en la tabla de opciones.
Comencemos creando una migración para agregar la columna:
rails g migration AddAnswerCounterCacheToOptions answers_count:integer
rake db:migrate
Luego le decimos a ActiveRecord que actualice la cuenta cuando creamos registros asociados, esto parece un poco extraño ya que counter_cache: true
la declaración está en belongs_to
lado mientras que la columna está en el otro, pero así es como funciona 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
Hay un pequeño inconveniente aquí. Dado que es posible que ya tengamos registros, debemos asegurarnos de que tengan los contadores correctos. Puede hacer esto desde la consola, pero a la larga es una buena idea crear una tarea de rake .
Option.find_each { |option| Option.reset_counters(option.id, :answers) }
Esto puede tomar un poco de tiempo ya que necesita extraer cada opción y actualizar el conteo.
Ahora podemos mostrar la cuenta así:
<% 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
es lo suficientemente inteligente como para usar nuestra columna de caché de contadores, pero recurrirá a consultar el conteo, lo cual es bueno para las pruebas.