2009-02-24 3 views
12

Я хочу отображать список с тегами и количество элементов (в моем примере «Задачи») для каждого тега.Rails: find_by_sql и виртуальный столбец

Для этого я создал следующий метод в моей модели Tag:

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 

Метод возвращает правильные имена тегов, но по какой-то причине taskcounts не в результате. Результат выглядит

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

Поскольку этот подход не похоже на работу, я задаюсь вопросом, что Rails-способ выполнить такую ​​задачу. Благодаря!

+0

Отличный вопрос! Я знаю, что это старый пост, но он спас меня в большом проекте, над которым я работаю ... так, спасибо! – dennismonsewicz

ответ

21

Количество есть, вы просто не можете увидеть его, поскольку taskcount не является атрибутом Rails создает для этого класса Task, потому что это не столбец, что он может видеть. Вы должны использовать вызов атрибутов, чтобы найти его. Образец:

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

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

Спасибо .. это было полезно – bragboy

9

"Rails путь" является использование 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 

Это требует добавления tag_tasks_count колонки на вашем столе 'Tag'.

Если добавить named_scope в тег как так:

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

Затем вы можете заменить все Tag.find_by_count с Tag.active. Используйте его так:

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

Не заметил, что счетчик кэша, или действительно назвал области. Очень приятно - объясняет. –

+0

Спасибо за идею с кешем-счетчиком, который кажется более чистым решением, чем использование find_by_sql. – dhofstet

Смежные вопросы