Forcibly Set Counter Cache Columns to Zero in Rails 5

in code


(Or any other value you like.)

So it turns out that counter cache columns are marked as readonly because Rails handles increment/decrement internally through methods such as increment_counter. Rails does not want you to update the counters by hand. It really wants you to leave the integrity of those fields to the framework itself. Every update_whatever method (even update_column) fails. The only way I found which works is a straight-up raw SQL write.

# Dirty hack to forcibly set readonly counter-cache attributes to zero.
#
# @see https://apidock.com/rails/ActiveRecord/CounterCache/reset_counters
# @param record [ApplicationRecord::*] Any kind of application record.
# @param counters [Array<Symbol>] List of counter-cache columns to zero out.
# @return record [ApplicationRecord::*] Reloaded record with zeroed counters.

def set_counters_to_zero!(record, *counters)
  table_name = record.class.table_name
  zeroed_counters = counters.flatten.map { |field| "`#{field}` = 0" }.join(',')

  ActiveRecord::Base.connection.execute <<-eos
    UPDATE `#{table_name}`
    SET #{zeroed_counters}
    WHERE `#{table_name}`.`id` = #{record.id}
  eos

  record.reload
end

You should never ever ever use this code in production. It breaks data integrity. I wrote it to test certain jobs and model callbacks, and it does by forcibly zeroing-by deliberately breaking-the counters.



instasort 4.0.1

in code


Your email address will not be published. Required fields are marked *