Rails: counter_cache no activa la devolución de llamada after_update
-
29-10-2019 - |
Pregunta
Entonces, tengo modelos Folder y 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
Estoy usando un counter_cache para mantener el número de itens de una sola carpeta.Pero una carpeta puede ser la principal de otra carpeta, y quería que la carpeta principal tuviera la suma de counter_cache de todos sus hijos más su propio counter_cache.
Para hacerlo, intenté poner un método after_update que almacena en caché los cambios realizados en la columna counter_cache, pero de alguna manera este método no se llama cuando se crea un nuevo FolderItem.
Solución
Haría algo como esto.
Agregue algunos campos de contador de caché a la tabla de carpetas
$ 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
Y código 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
Tenga en cuenta que el método Folder#sum_of_children
es recursivo, por lo que para grandes conjuntos de datos puede ralentizar su aplicación.Es posible que desee hacer algo más de magia SQL, pero como Ruby puro, esto es lo más cercano a una buena solución que puedo obtener.Vi que lo hiciste al revés, eso será tan lento como necesites actualizar desde abajo hacia arriba también.(Esto es de arriba hacia abajo)
No sé si eso es lo que está buscando, pero es una solución legible para almacenar en caché la cantidad de elementos dentro de una carpeta.