2009-02-24 14 views
12

Quiero mostrar una lista con etiquetas más el número de elementos (en mi ejemplo "Tareas") para cada etiqueta.Rieles: find_by_sql y la columna virtual

Para ello creé el método siguiente en mi modelo de etiqueta:

def self.find_with_count 
    find_by_sql 'SELECT 
       Tag.name, 
       COUNT(Tag.name) AS taskcount 
       FROM 
       tags AS Tag 
       INNER JOIN tags_tasks tt ON tt.tag_id = Tag.id 
       INNER JOIN tasks t ON tt.task_id = t.id 
       WHERE 
       t.finished = 0 
       AND t.deleted = 0 
       GROUP BY 
       Tag.name 
       ORDER BY 
       Tag.name' 
end 

el método devuelve los nombres de las etiquetas correctas, pero por alguna razón los taskcounts no están en el resultado. El resultado se parece

[#<Tag name: "hello">, #<Tag name: "world">] 

Como no parece funcionar este enfoque, me pregunto lo que el Rieles de ida es para llevar a cabo tal tarea. ¡Gracias!

+0

Excelente pregunta! Sé que esta es una publicación anterior, pero me ahorró un gran proyecto en el que estoy trabajando ... ¡así que gracias! – dennismonsewicz

Respuesta

21

El recuento está ahí, simplemente no se puede ver que ya taskcount no es un atributo Carriles crea para esa clase de tareas, ya que no es una columna que se puede ver. Tienes que usar la llamada de atributos para encontrarlo. muestra:

class Tag < ActiveRecord::Base 
    ... 
    def taskcount 
    attributes['taskcount'] 
    end 
end 

Tag.find_with_count.each do |t| 
    puts "#{t.name}: #{t.taskcount}" 
end 
+0

Gracias ... eso fue útil – bragboy

9

El "camino carriles" es utilizar un counter_cache:

class Tag < ActiveRecord::Base 
    has_many :tag_tasks 
    has_many :tasks, :through => :tag_tasks 
end 

# the join model 
class TagTask < ActiveRecord::Base 
    belongs_to :tag, :counter_cache => true 
    belongs_to :task 
end 

class Task < ActiveRecord::Base 
    has_many :tag_tasks 
    has_many :tags, :through => :tag_tasks 
end 

Para ello hay que añadir una columna tag_tasks_count en su mesa 'Tag'.

Si agrega un named_scope para etiquetar así:

class Tag ... 
    named_scope :active, lambda { { :conditions => { 'deleted' => 0, 'finished' => 0 } } } 
end 

A continuación, puede reemplazar todas Tag.find_by_count con Tag.active. Utilizar de esta manera:

Tag.active.each do |t| 
    puts "#{t.name} (#{t.tag_tasks_count})" 
end 
+0

No se ha detectado el contador de caché, o incluso ámbitos nombrados. Muy lindo, gracias por explicarme. –

+0

Gracias por la idea con el contador de caché, que parece ser una solución más limpia que usar find_by_sql. – dhofstet

Cuestiones relacionadas