rails
rails copied to clipboard
ActiveRecord: HMT with conditions does not update the in-memory collection when pushing new objects
Hi! When using a has_many :a, through: :b
relation, and the :b
relation has a condition, adding new objects doesn't update the in-memory collection properly (stops after the first object).
Steps to reproduce
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "rails", github: "rails/rails", branch: "main"
gem "sqlite3"
end
require "active_record"
require "minitest/autorun"
require "logger"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :packages, force: true do |t|
end
create_table :package_items, force: true do |t|
t.integer :package_id
t.integer :item_id
end
create_table :items, force: true do |t|
t.boolean "deleted", default: false
end
end
class Package < ActiveRecord::Base
has_many :package_items, ->{ where(item_id: Item.active.pluck(:id)) }
has_many :items, through: :package_items
end
class PackageItem < ActiveRecord::Base
belongs_to :package
belongs_to :item
end
class Item < ActiveRecord::Base
has_many :package_items
has_many :packages, through: :package_items
scope :active, -> { where(deleted: false) }
end
class BugTest < Minitest::Test
def test_association_stuff
package = Package.create!
package.items << Item.create!
package.items << Item.create!
package.items << Item.create!
# This should be [3, 3, 3] but we get [3, 1, 3]
assert_equal [3, 3, 3], [Item.count, package.items.count, package.reload.items.count]
end
end
Expected behavior
package.items.count
is equal to 3.
Actual behavior
package.items.count
is equal to 1, even though all the items and relations are created properly (see assertion).
System configuration
Rails version: tested on 7.1.0.alpha
and 6.1.6.1
.
Ruby version: tested on 2.7.5
.
Workaround
I was able to fix this issue by changing the weird condition to a proper scope:
class Package < ActiveRecord::Base
has_many :package_items, ->{ includes(:item).merge(Item.active) }
has_many :items, through: :package_items
end