ActiveRecord has_many:通过复制质量分配的计数器缓存

时间:2012-02-28 03:39:45

标签: ruby-on-rails ruby activerecord has-many-through

似乎ActiveRecord的counter_cache功能可能导致计数器缓存递增两次。我看到这种行为的情况是当我有两个模型通过连接模型彼此具有has_many :through关系时(即:Teacher有许多Student到{{1 }})。使用Classroom生成的方法直接关联教师和学生(无需手动创建连接记录)时,计数会增加2倍。示例:has_many :through导致teacher.students << Student.create(name: "Bobby Joe")增加2。

请允许我找到一个解决方案来减轻或消除此问题,同时允许我通过teacher.students_count关系继续使用内置计数器缓存和质量分配。

我花了很多时间寻找解决方案并将问题提取到一个小型测试应用程序,这是我可以创建的最简单的失败示例。任何有助于我解决此问题的其他细节都应该在下面。

示例模式和模型:

has_many :through

这是一个简短的rails控制台会话,显示了所采取的步骤以及rails正在执行两次更新create_table :teachers do |t| t.string :name t.integer :students_count, default: 0 t.timestamps end class Teacher < ActiveRecord::Base has_many :classrooms has_many :students, :through => :classrooms end create_table :students do |t| t.string :name t.integer :teachers_count, default: 0 t.timestamps end class Student < ActiveRecord::Base has_many :classrooms has_many :teachers, :through => :classrooms end create_table :classrooms do |t| t.references :teacher t.references :student t.timestamps end class Classroom < ActiveRecord::Base belongs_to :student, :counter_cache => :teachers_count belongs_to :teacher, :counter_cache => :students_count end 以增加teachers的事实:

students_count

如果有人想要仔细观察(https://github.com/carlzulauf/test_app),我已将整个测试应用程序放在github上。我还创建了一个单元测试来演示问题并且无法通过(https://github.com/carlzulauf/test_app/blob/master/test/unit/classroom_test.rb

1 个答案:

答案 0 :(得分:12)

到目前为止,我的研究告诉我这可能是一个错误。以下是针对此问题已提交的一些github问题:

https://github.com/rails/rails/issues/3903

https://github.com/rails/rails/issues/3085

显然有一个由has_many:通过关系引起的无证自动计数器缓存。因此,如果Teacher.has_many :students, :through => :classrooms,那么teacher.students << student集合分配已经查找,如果该列存在则会增加teacher.students_count

如果添加Classroom.belongs_to :teacher, :counter_cache => :students_count,则会在创建“课堂”模型时触发其他回调,并且该列会增加两次

有效解决方法:将计数器缓存列重命名为其他内容。 Student#teacherz_countTeacher#studentz_count有效允许test case通过。

https://github.com/carlzulauf/test_app/commit/707a33f948d5d55a8aa942e825841fdd8a7e7705

我还没有找到问题在ActiveRecord代码库中的位置,所以我暂时不会接受我自己的答案,以防有人知道为什么has_many :through以这种方式工作以及违规代码的存在。

更新

我相信我发现了令人讨厌的代码行。注释掉这一行可以解决问题:

https://github.com/rails/rails/blob/889e8bee82ea4f75adb6de5badad512d2c615b7f/activerecord/lib/active_record/associations/has_many_through_association.rb#L53

我似乎无法启动并运行边缘导轨,因此我无法提交此错误。如果其他人能够,请做。

查找违规行允许我在我的测试应用中制作更有效的猴子补丁,解决问题而无需重命名任何列。

https://github.com/carlzulauf/test_app/commit/3c421b035bd032b91ff60e3d74b957651c37c7fa