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
Метод возвращает правильные имена тегов, но по какой-то причине количество задач не является результатом. Результат выглядит как
[#<Tag name: "hello">, #<Tag name: "world">]
Поскольку этот подход не работает, мне интересно, как Rails-путь должен выполнить такую задачу. Спасибо!
Ответы
Ответ 1
Счет есть, вы просто не можете его увидеть, поскольку 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
Ответ 2
"Rails way" - использовать 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