44

我要'耙子中止了!...posts_count 被标记为只读的错误。

我有两个模型:用户和帖子。

users has_many posts.

posts belongs_to :user, :counter_cache => true

我有一个迁移,它将 posts_count 列添加到 users 表中,然后计算并记录每个用户的当前帖子数。

self.up
  add_column :users, :posts_count, :integer, :default => 0

  User.reset_column_information
  User.all.each do |u|
    u.update_attribute( :posts_count, u.posts.count)
  end
end

当我运行迁移时,我得到了错误。当然,这是非常明确的,如果我从帖子模型中删除 :counter_cache 声明,例如

belongs_to :user

迁移运行良好。这显然没有意义,因为您无法以这种方式真正实现它。我错过了什么?

4

2 回答 2

98

您应该使用它User.reset_counters来执行此操作。此外,我建议使用find_each而不是,each因为它会批量迭代集合,而不是一次全部迭代。

self.up
  add_column :users, :posts_count, :integer, :default => 0

  User.reset_column_information
  User.find_each do |u|
    User.reset_counters u.id, :posts
  end
end
于 2010-11-28T05:14:05.123 回答
3

好的,文档指出:

计数器缓存列通过 attr_readonly 添加到包含模型的只读属性列表中。

我认为会发生这种情况:您在模型的定义中声明计数器,从而将“posts_count”属性呈现为只读。然后,在迁移中,您尝试直接更新它,导致您提到的错误。

快速而肮脏的解决方案是从模型中删除 counter_cache 声明,运行迁移(为了将所需的列添加到数据库并使用当前的帖子计数填充它),然后将 counter_cache 声明重新添加到该模型。应该可以工作,但很讨厌,并且在迁移过程中需要人工干预——这不是一个好主意。

我发现这篇博客文章建议在迁移期间更改模型的只读属性列表,它有点过时,但您可能想尝试一下。

于 2010-11-28T04:46:20.637 回答