2012-02-22 2 views
1

Итак, у меня есть папка и модели FolderItem.Rails: counter_cache не запускает обратный вызов after_update

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 всех ее дочерних элементов, а также собственный счетчик_пакетов.

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

+0

Некоторые фактические код, чтобы узнать, почему он не называется будет здорово. Как и класс FolderItem? Это похоже на то, что вам придется писать полный код кэширования, хотя, потому что counter_cache не был создан ни для чего другого, кроме Folder.has_many: items. Что вы делаете, это Folder.has_many: папки, которые на самом деле имеют много папок. – tombruijn

+0

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

ответ

1

Я бы сделал что-то вроде этого.

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

$ 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 

и Руби код

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, это как можно ближе к хорошему решению. Я видел, что вы сделали это наоборот, это будет так же медленно, как вам нужно обновить и снизу вверх. (Это сверху вниз)

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

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