恢复具有偏执的记录后更新计数器缓存列(acts_as_paranoid)

时间:2014-08-24 08:19:24

标签: ruby-on-rails ruby-on-rails-4 counter-cache acts-as-paranoid

我的模型中有counter_cache列。我对此模型使用acts_as_paranoidParanoia gem)。如何在还原记录时更新关联记录的计数器缓存列?

3 个答案:

答案 0 :(得分:1)

您可以使用before_restore回调。将.increment_counter method放在此before_restore回调中的相关记录中。

reset_counters method+= 1无法工作。

答案 1 :(得分:1)

以下是此问题的模型关注解决方案。

如果您遇到此问题,您应该已经安装了偏执宝石但是为了完整起见,请在您的Gemfile中包含偏执宝石并使用'bundle'命令安装它。

# Gemfile
gem 'paranoid'

创建一个问题。

# app/models/concerns/paranoid_deletable.rb
module ParanoidDeletable
  extend ActiveSupport::Concern

  included do
    # activate the paranoid behavior
    acts_as_paranoid
    # before restoring the record, manually increment the counter
    before_restore :increment_counter_cache
  end

    module ClassMethods

      def counter_column_name
        "#{self.name.underscore.pluralize}_count"
      end

    end

    def counter_associations
        associated_counters = []
        # get all belongs_to associations
        self.reflect_on_all_associations(:belongs_to).collect do |association|
          return unless association.options[:counter_cache]
          associated_klass_name = association.options[:polymorphic] ? self.send("#{association.name}_type") : association.class_name
          associated_klass_name.constantize.column_names.each do |column_name|
            # collect the association names and their classes if a counter cache column exists for this (self) class.
            associated_counters << { association_name: association.name, klass_name: associated_klass_name } if(column_name == self.class.counter_column_name)
          end
        end
        # return the array of { association_name, association_klass } hashes
        associated_counters
      end

    private

    def increment_counter_cache
      # before restore...
      self.counter_associations.each do |counter_association|
        association_name = counter_association[:association_name]
        klass_name = counter_association[:klass_name]
        # ...increment all associated counters 
        klass_name.constantize.increment_counter(self.class.counter_column_name.to_sym, self.send("#{association_name}_id".to_sym))
      end
    end
  end

然后在你的模特中。

# app/models/post.rb
class Post < ActiveRecord::Base
  include ParanoidDeletable
  belongs_to :user, :counter_cache => true
  ...
end

一些设置说明:

1)您的复数belongs_to:association_name必须与您的计数器缓存列名称匹配:“#{association_name} _count”。例如:

# Will work
console > user.posts_count
          => 212
# Won't work
console > user.how_much_they_talk_count
          => 212

2a)如果您使用的是多态关系,则需要在两个模型中正确设置关联。例如:

# app/models/post.rb
....
has_many :comments, as: :commentable
....

# app/models/comment.rb
...
belongs_to :commentable, polymorphic: true, counter_cache: true
...

2b)如果您使用的是多态关系,则需要按如下方式命名引用类型字段:“#{association_name} _type”。例如:

# Will work
console > comment.commentable_type
          => "Post"
# Won't work
console > comment.commentable_class_name
          => "Post"

免责声明:我试图制作这种模块,但这是第一次通过,我没有彻底测试过。

答案 2 :(得分:1)

这是一个非常简单的解决方案,但有些人不同意语义。

module ActiveRecord
  # trigger create callbacks for counter_culture when restoring
  class Base
    class << self
      def acts_as_paranoid_with_counter_culture
        acts_as_paranoid
        simulate_create = lambda do |model|
          model.run_callbacks(:create)
          model.run_callbacks(:commit)
        end
        after_restore(&simulate_create)
      end
    end
  end
end

然后,您只需将acts_as_paranoid的调用替换为acts_as_paranoid_with_counter_culture