Rails: counter_cache не запускает обратный вызов after_update - PullRequest
1 голос
/ 22 февраля 2012

Итак, у меня есть модели Folder и FolderItem.

UPDATE

# == Schema Information
#
# Table name: folders
#
#  id                 :integer         not null, primary key
#  name               :string(255)     not null
#  parent_folder_id   :integer
#  user_id            :integer         not null
#  folder_itens_count :integer         default(0)
#  created_at         :datetime
#  updated_at         :datetime
#

class Folder < ActiveRecord::Base
...

  belongs_to :parent_folder, :class_name => 'Folder'
  has_many :child_folders, :class_name => 'Folder', :foreign_key => :parent_folder_id
  has_many :folder_itens, :order => 'created_at DESC'

  after_update {
    update_parent_folder_itens_count
  }

  def update_parent_folder_itens_count
    parent_folder = self.parent_folder
    if self.folder_itens_count_changed? && parent_folder
      quant_changed = self.folder_itens_count - self.folder_itens_count_was
      parent_folder.increment(:folder_itens_count, quant_changed)
    end
  end
end

class FolderItem < ActiveRecord::Base
... 
  belongs_to :folder, :counter_cache => :folder_itens_count
end

Я использую counter_cache, чтобы сохранить количество itens одной папки. Но папка может быть родительской для другой папки, и я хотел, чтобы родительская папка имела сумму counter_cache всех ее дочерних элементов плюс собственный counter_cache.

Для этого я попытался поместить метод after_update, кэширующий изменения, сделанные в столбце counter_cache, но каким-то образом этот метод не вызывается при создании нового FolderItem.

1 Ответ

1 голос
/ 23 февраля 2012

Я бы сделал что-то подобное.

Добавление некоторых полей счетчика кэша в таблицу папок

$ rails g migration add_cache_counters_to_folders child_folders_count:integer \
                                                  folder_items_count:integer  \
                                                  total_items_count:integer   \
                                                  sum_of_children_count:integer

и код Ruby

class Folder < ActiveRecord::Base
  belongs_to :parent_folder, class_name: 'Folder', counter_cache: :child_folders_count
  has_many :child_folders, class_name: 'Folder', foreign_key: :parent_folder_id
  has_many :folder_items

  before_save :cache_counters

  # Direct descendants - files and items within this folder
  def total_items
    child_folders_count + folder_items_count
  end

  # All descendants - files and items within all the folders in this folder
  def sum_of_children
    folder_items_count + child_folders.map(&:sum_of_children).inject(:+)
  end

private
  def cache_counters
    self.total_items_count = total_items
    self.sum_of_children_count = sum_of_children
  end
end

class FolderItem < ActiveRecord::Base
  belongs_to :folder, counter_cache: true # folder_items_count
end

Обратите внимание, что метод Folder#sum_of_children является рекурсивным, поэтому для больших наборов данных он может замедлить работу вашего приложения. Возможно, вы захотите сделать с ним больше магии SQL, но для чистого Ruby это как можно ближе к хорошему решению. Я видел, что вы сделали это с другой стороны, это будет так же медленно, как и обновление снизу вверх. (Это сверху вниз)

Не знаю, ищите ли вы это, но это удобочитаемое решение для кэширования количества элементов в папке.

...