Rails: counter_cache löst keinen after_update-Rückruf aus
-
29-10-2019 - |
Frage
Ich habe also ein Folder- und ein FolderItem-Modell.
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
Ich verwende einen counter_cache, um die Anzahl der itens eines einzelnen Ordners zu speichern.Aber ein Ordner könnte der übergeordnete Ordner eines anderen Ordners sein, und ich wollte, dass der übergeordnete Ordner die Summe aus counter_cache aller untergeordneten Ordner und dem eigenen counter_cache enthält.
Zu diesem Zweck habe ich versucht, eine after_update-Methode einzufügen, die die in der Spalte counter_cache vorgenommenen Änderungen zwischenspeichert. Diese Methode wird jedoch beim Erstellen eines neuen FolderItem nicht aufgerufen.
Lösung
Ich würde so etwas tun.
Fügen Sie der Ordnertabelle einige Cache-Zählerfelder hinzu
$ 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
Und Ruby-Code
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
Bitte beachten Sie, dass die Folder#sum_of_children
-Methode rekursiv ist, sodass sie bei großen Datenmengen Ihre Anwendung verlangsamen kann.Vielleicht möchten Sie etwas mehr SQL-Magie daraus machen, aber als reiner Ruby ist dies einer guten Lösung so nahe wie möglich.Ich habe gesehen, dass Sie es umgekehrt gemacht haben, das wird genauso langsam sein, wie Sie es auch von unten nach oben aktualisieren müssen.(Dies ist von oben nach unten)
Sie wissen nicht, ob Sie danach suchen, aber es ist eine lesbare Lösung zum Zwischenspeichern der Anzahl der Elemente in einem Ordner.