【问题标题】:Rails 4: counter shows "0" instead of actual "size" when using counter_cacheRails 4:使用 counter_cache 时,计数器显示“0”而不是实际“大小”
【发布时间】:2016-05-24 10:54:16
【问题描述】:

我在论坛表格中添加了forum_threads_countforum_posts_count 列。 forum_threads_count 工作得很好。 forum_posts_count 已重置为“0”,而不是显示在我添加计数器缓存列之前创建的所有论坛帖子。这些关系是:Forum has_many :forum_threads、ForumThreads has_many :forum_posts 和 Forum has_many :forum_posts, through: :forum_threads

后来我发现我不能将 counter_cache 与has_many through: 关系一起使用。所以我写了一些私有方法来添加after_create/after_destroy 调用来增加/减少计数器。计数器有效,只是它仍然没有考虑在将这些列添加到论坛表之前创建的所有论坛帖子。我觉得我编写迁移的方式有问题。请帮助并提前感谢您。我感谢这个网站上帮助人们的每一个人。

"...add_counters_to_forums_table.rb"(迁移文件)

class AddCountersToForumsTableAgain < ActiveRecord::Migration

def self.up
 change_table :forums do |t|
   t.integer :forum_threads_count, :forum_posts_count, default: 0
 end

  Forum.reset_column_information

  Forum.all.pluck(:id).each do |id|
   Forum.reset_counters(id, :forum_posts)
   Forum.reset_counters(id, :forum_threads)
  end
end

 def self.down
  change_table :forums do |t|
   t.remove :forum_threads_count, :forum_posts_count
  end
 end

end

models/forum.rb

class Forum < ActiveRecord::Base

 has_many :forum_threads, -> { order ('updated_at DESC') }, dependent: :destroy 

 accepts_nested_attributes_for :forum_threads
 has_many :forum_posts, through: :forum_threads
 accepts_nested_attributes_for :forum_posts

end

models/forum_thread.rb

class ForumThread < ActiveRecord::Base

 belongs_to :user
 belongs_to :forum, counter_cache: true
 has_many :forum_posts, dependent: :destroy
 accepts_nested_attributes_for :forum_posts

end

models/forum_post.rb

class ForumPost < ActiveRecord::Base

 belongs_to :forum_thread, touch: true
 belongs_to :forum 
 belongs_to :user

  after_create :increment_forum_posts_count
  after_destroy :decrement_forum_posts_count

private

 def increment_forum_posts_count
  Forum.increment_counter( 'forum_posts_count', self.forum_thread.forum.id )
 end

 def decrement_forum_posts_count
  Forum.decrement_counter( 'forum_posts_count', self.forum_thread.forum.id )
 end

end

观点/论坛/index.html.erb

<%= render 'shared/page_title', title: "Forums" %>
<div class="col-md-10 col-md-offset-1">
<div class="actions">
 <%= link_to "Create New Forum", new_forum_path, class: 'btn btn-primary' %>

    <div class="pull-right">
        <%= form_tag @forum_thread, method: :get do |f| %>
            <%= text_field_tag :q, nil, class: 'form-control', placeholder: 'Search...' %>
        <% end %>
    </div>
</div>

 # LIST FORUMS WITH THREADS AND POSTS COUNTER CACHE
<div class="list-group">
    <% @forums.each do |forum| %>
            <a href="<%= forum_forum_threads_path(forum.id, @forum_threads) %>" class="list-group-item">                
                <h3><%= forum.title %>
                    <div class="pull-right small">                  
                        <%= pluralize forum.forum_threads.size, 'thread' %> |                           
                        <%= pluralize forum.forum_posts.size, 'post' %>                         
                    </div>
                </h3>               
        </a>            
    <% end %>
</div>

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 caching


    【解决方案1】:

    您能否提供更多信息,为什么您不能将计数器缓存用于:through 关系?查看reset_counter方法,好像有处理这种情况的代码:

    # File activerecord/lib/active_record/counter_cache.rb, line 20
    def reset_counters(id, *counters)
      object = find(id)
      counters.each do |counter_association|
        has_many_association = _reflect_on_association(counter_association)
        unless has_many_association
          has_many = reflect_on_all_associations(:has_many)
          has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
          counter_association = has_many_association.plural_name if has_many_association
        end
        raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
    
        if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
          has_many_association = has_many_association.through_reflection
        end
    
        foreign_key  = has_many_association.foreign_key.to_s
        child_class  = has_many_association.klass
        reflection   = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
        counter_name = reflection.counter_cache_column
    
        stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
          arel_table[counter_name] => object.send(counter_association).count(:all)
        }, primary_key)
        connection.update stmt
      end
      return true
    end
    

    所以我会尝试更新到最新的稳定 Rails 版本,在该列上运行 reset_counter 并再次检查。

    历史表明,rails 在计数器方面存在许多微妙问题,建议您不时调整计数,因为它们会不同步(即人们手动从数据库中删除记录)。

    【讨论】:

    • 我对 Rails 还是很陌生。我正在使用 Rails 4.2.1。我将如何下载最新的稳定版本?另外,我关注了this tutorial,forum_posts_count 返回了 forum_threads 的数量,而不是 forum_posts。 @phoet
    【解决方案2】:

    必须编写另一个迁移来处理计数器本身。

    class CacheForumForumPostsCount < ActiveRecord::Migration
    
    def up
      Forum.find_each do |forum|
        forum_threads = ForumThread.where(forum_id: forum.id)
        post_counter = 0
        forum_threads.each do |ft|
            post_counter += ft.forum_posts.size
        end
        forum.update_attributes(forum_posts_count: post_counter)
     end
    end
    
     def down
    
     end
    end
    

    【讨论】:

      猜你喜欢
      • 2021-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多